
Nerdsniping: A glimpse into a stubborn mind - philip_roberts
https://blog.andyet.com/2014/04/07/nerdsniping
======
vernie
Yikes. I understand that companies use these posts as a form of advertising,
but seeing your "application security specialist" struggling with such a basic
expression gives me pause.

------
romanovcode
I was working with this guy - he always tried to prove that everything is
possible wasting time. He ALWAYS over-complicated simple things. He was
horrible to work with and everyone hated him.

Not to say you are just like him etc. but this post reminded me of him because
it was probably exactly what he would do.

~~~
kbenson
Wasting other people's time is a negative, but deepening your understanding of
a topic by examining edge cases is generally worthwhile, IMHO.

~~~
spenuke
Is it really fair to call poor code an edge case? If not for the double-equals
typo, which most code reviews would fix, this wouldn't work.

I'm more interested that this team has conversations where person A asks a
really simple question that should be able to be tested in 11 seconds, person
B gives a patently false answer that mistakes && for ||, which is the very
thing person A is asking about. Meanwhile person C goes and spends 11 days
looking for a loophole and writing a blog about it.

As a person gearing up for my first web dev position, I really wonder if this
is what constitutes professional Javascript development and ask myself if I
shouldn't be applying to jobs already.

~~~
kbenson
No, person C saw an interesting question, and decided to go on an exploratory
journey _to see what 's possible._ In any job requiring mental aptitude,
you'll find natural curiosity an asset, as long as you keep it within bounds.

To be clear, anyone that's presented a question about a tool they are using
that may pertain to it's use (be it efficiency or correctness) that shows zero
interest in the answer (whether or not they have time to pursue an answer) is
not someone I want to work with or manage, and should probably find another
career, because the one they are in isn't holding their interest. Work doesn't
have to be the most important thing to you, but if you don't like your job,
the people around you WILL notice.

~~~
spenuke
I'm not sure your second paragraph is clear, to me, at least. Person C
admitted that their only contribution to the conversation was to announce that
they had been nerd-sniped. Which means that they showed zero interest in the
"question they were presented with", because they were sidetracked by another,
more interesting (to them) problem.

Meanwhile, person A's question went unanswered. I would consider this a case
of not keeping curiosity within bounds. Wouldn't you?

I agree wholeheartedly that curiosity is a great motivator, one that I would
want driving all my coworkers. But as this blog presents it, the curiosity
suffers from the greatest threat to curiosity, which is a lack of discipline
that ensures the curious person never rises above a dilettante. I speak from
first hand knowledge here - I've wasted more than my share of time doing
something just for fun that benefits nobody.

If it were not the case that Person B, the implicit authority figure in the
scenario, gave a totally backwards answer to Person A, OR ( || ) if it were
not the case that Person C failed to alleviate the confusion of Persons A and
B, then I would not see any problem with going off on a trip down pedantry
lane. As it is, the whole organization looks like a gang of clowns. Nobody can
answer the most basic of syntax questions, and one person even takes a faulty
intuition ("b can be both 80 and 443") and follows it on a Quixotic quest into
the arcana of the language spec. It's almost as though he stumbled on the real
value of the quest (how evaluations of == and === are actually implemented) by
sheer accident, in spite of himself. Luckily, many quests follow this pattern.

For what it's worth, I do find the discussion interesting. But it's
interesting in way different than that of edge cases. Deliberately breaking
something by using it in a way it was never intended to be used can be
enlightening if you want to fend off would-be attackers. It exploits the
system's implementation constraints. On the other hand, an edge case
highlights the edges of legitimate use which the system was never designed to
account for. It exploits the system's lack of comprehensiveness. The
distinction is a good one, I think.

For example, consider a chair. An edge case might be a 600-pound man. The
engineers didn't take this legitimate use of the chair into account when they
determined the thickness of the material. That's an edge case. It's not quite
the same as saying that the engineers didn't take into account that wood is
flammable. This example is not the best, but I'm now well too far into my own
trip down pedantic lane to spend any more time trying to improve it. :)

~~~
philip_roberts
It's not made clear from my post, because I felt it was entirely irrelevant to
the context of the post itself (which was merely intended as an interesting
meander through the thought process and experiments my mind went through over
time) but I stripped a number of comments from the screencap wherein the
question was in fact answered.

Had I suspected the quality of my team, or my discipline would be under
question, I would have left them in :)

------
mncolinlee
And this is why Javascript can be such an awful language. Leaving off a single
equals has such a profound effect, often without a new coder even realizing it
while reading the code.

Imagine what happens to your code when someone tries to write a function for
its side effects similar to the example seen in the blog post. Then releases
it in production for it to break in six months with a feature change.

~~~
pmelendez
>"And this is why Javascript can be such an awful language."

To be fair, this is breaking Javascript and that is something you could do in
other languages, for instance here is a similar version for C++:

[http://pastebin.com/seySfpku](http://pastebin.com/seySfpku)

~~~
mncolinlee
Pedantry aside... are you really going to defend Javascript's core method of
function handling as good design by using the C function pointer as an
example?

On some level, defending JS using C is like saying you can also make a souffle
by instead cooking the chicken with the egg still inside it. Both JS and its
bad paradigms came from C. You don't arrive at good design by showing that
other people also got it wrong.

~~~
pmelendez
I think you are missing my higher point. I am not defending JS or C++ for that
matter (please note that I was not using C), but the fact that giving enough
time you can break most languages to do something silly, in which case I don't
blame the tool but the people using it.

------
ronaldx
The differences between programming syntax and elementary/Boolean algebra are
very awkward to understand clearly and deal with correctly. We clearly
understand mathematical algebra to work one way, and we naturally assume
programming algebra works the same way, which it doesn't at all.

Here, the antagonist says that b can't be 1 and 2 at the same time, which
would be self-evident in mathematical algebra, but turns out to be quite
irrelevant to Javascript and to programming paradigms generally (since two
statements will never be checked simultaneously).

This difference in how syntax is understood actually presents a barrier to
programming for modestly trained mathematicians, who would otherwise be
expected to excel.

~~~
nmrm
>This difference in how syntax is understood actually presents a barrier to
programming for modestly trained mathematicians, who would otherwise be
expected to excel.

Although this is true in general that syntax can be a barrier to entry, the
article provides a particularly poor example. If anything, the article
demonstrates how mathematical training is good preparation for many pitfalls
of programming.

Mathematicians are used to working with various equivalence relations, even in
the same context. So if a modestly trained mathematician saw == and ===, she
would immediately ask "what is the difference between these two ER's?".

And then when she finds out the essential difference is that you can override
==, it would be clear that all bets are off.

I can't imagine any mathematician taking more than a few minutes and a google
search -- let alone 11 days -- to figure out this loophole.

~~~
Dewie
> I can't imagine any mathematician taking more than a few minutes and a
> google search -- let alone 11 days -- to figure out this loophole.

If anything, mathematicians are incredibly used to overloaded equals
operators.

~~~
nmrm
> If anything, mathematicians are incredibly used to overloaded equals
> operators.

That's my point. The == sign meaning many things is unsurprising (and key to
this "hack"), whereas explicitly using two different symbols for equivalence
in the same expression is a huge, glaring signpost to pay attention.

~~~
resu_nimda
As demonstrated in the postscript, it can still be accomplished when the
symbols are all the same. So that difference is not the key to the hack. As
ronaldx noted, the key is that a variable can change value in between two
equivalence evaluations within a single statement.

~~~
philip_roberts
Arguably the thing that tripped me up for the majority of the time was
latching on to the fact that == and === are different, and trying to _always_
satisfy the two conditions, rather than just spotting that I can change the
reference to b from within the first valueOf call. I'm not sure how much I'd
read into the "if I was more of a mathematician and less of a dumb programmer
this would have been solved trivially" argument.

~~~
nmrm
If a == b iff a === b, then == ~ ===. If == and === are different (which they
presumable are, because otherwise we would only use one symbol) then by
definition there exist counter-examples of the thing you were trying to
_always_ satisfy.

In other words, it would never even occur to a mathematician to make your
mistake...

More to the point, a mathematician wouldn't assume that some arbitrary
predicate that you are allowed to define at will behaves the same way as a
system-defined equivalence operator.

Edit: Added last paragraph and readability changes. Also, ~ is another symbol
for equality, this time equality over binary relations.

------
gyepi
Nerdsniping reminds me of the Walt Whitman poem:

    
    
       There was a child went forth every day;
       And the first object he look’d upon, that object he became;
       And that object became part of him for the day, or a certain part of the day, or for many years, or stretching cycles of years.
    

It's especially bad if you'd really rather be doing something other than what
you're currently doing.

------
tatalegma
"I want my code to execute if port is 80 or 443, and http is false"

Couldn't you simply do this?:

if ((a==='80'||b==='443') && http===false) { ... }

~~~
cowsandmilk
I think the goal was

if (a==='80' || (b==='443'&&http===false)) {...}

~~~
ssharp
I think you're right, but it's funny that both the code and the written
explanation do not make that clear. Missing parenthesis in one case and
missing comma in the other.

I feel like I tend to use parenthesis when they are not required, but I err on
the side of caution and I think it makes things more readable.

~~~
blt
I use a lot of unneeded parentheses. Makes the intent of the program clear. If
the expression gets ugly, factor out subexpressions into boolean variables. If
you are depending on short circuiting to avoid computing costly subexpresions,
make the dependency explicit with a nested `if`.

I had a thought the other day: Complicated code should look complicated. Don't
try to hide your convoluted logic. In the end, someone will have to mentally
unpack it into the multi-line verbose version to understand it anyway.

~~~
methodover
That's an excellent thought.

I feel like programmers too often try to make something complicated "pretty"
\-- but really this just usually has the effect of making it that much more
difficult to understand.

------
level
Call me nitpicky, but it always bugs me when someone references some content,
but doesn't link to it. Specifically the XKCD and Stackoverflow links. Link to
the comic and question specifically, rather than making me go hunt for that
content.

------
alistairjcbrown
> I guess we’ve messed with the prototype too much, and JavaScript isn’t
> really convinced it’s still a proper number anymore.

You're comparing a literal number with an object, which are not the same type.

    
    
      2 === new Number(2)
      // => false
      2 == new Number(2)
      // => true

------
scjody
> Have I learned anything directly useful?

You probably haven't. But a less experienced programmer has learned why you
should always use ===.

~~~
adamman
The example at the end of the article shows how you can have the same problem
while always using ===.

~~~
pdpi
The example at the end of the article shows how you deserve all sorts of bad
things happening to you if you mutate values in a freaking getter.

------
tarpherder
Might not be Java but still interesting, it made me think of one of my
favorite little quirks in C++:

    
    
      #include <cmath>
      
      //Floating point model: Strict:Precise:Fast
      //Will code execute
      float a = nanf("");
      if (a == a)//S:No P:No F:Yes
          (do something);
    
      if (a != a)//S:Yes P:Yes F:No
          (do something);
    
      if (a < 0.f || a > 2.f)//S:No P:No F:Yes
          (do something);
    
      if (isnan(a))//S:Yes P:Yes F:Yes
          (do something);
    
    

NaN's (Not A Number) can propagate a long way through your code, possibly
reaching places where they cause real problems. When dealing with input,
especially networking, one should always check for NaN's. Basically the rule
with NaN's is: The comparison always returns false if any NaN is involved. But
as you can see; specifying the fast model throws that out of the window. (Code
was otherwise unoptimized.)

In the third statement an otherwise fine check is done to make sure the value
in a is sane, it doesn't get changed but its certainly not what you'd want it
to be.

Whole lots of fun can be had when serving this to game servers. :D

------
mfonda
I think there was a more general question here that was missed: can a == x &&
a == y ever be true for any arbitrary values of a, x, and y, where x != y.

From a logical point of view, no, this can never be true. I would suspect this
can never be true in javascript, and could only be made true in a language
where you can override == to always return true.

I think when most developers use the word "never", what they really mean is
"never (within the current context)". This makes conversations a lot simpler.
Imagine how difficult conversations would be if you always had to qualify
never. "This can never be true (assuming a weird valueOf method hasn't been
defined and assuming I didn't modify the javascript interpreter to always
return true for == and assuming ...)".

~~~
lmkg
I think there are a few cases of non-transitive equalities in JavaScript,
specifically around falsey values. Part of it also has to do with whether x is
the right-side input or the left-side input, because that changes type
coercion rules.

Aha, found one:

    
    
      ['0'] == 0
      > true
      [0] == 0
     > true
      [0] == ['0']
     > false
    

Transitivity of equality can also never be relied upon when dealing with
Floating Point numbers. This is correct and unavoidable behavior, but still
surprising to many developers.

~~~
gsg
That's not due to transitivity: ['0'] == ['0'] (and for that matter, ['0'] ===
['0']) is also false. These operators compare objects for identity, not
structural equality.

------
aaronem
It's not often I see an HN thread which so aptly demonstrates the exact effect
under discussion.

------
antinitro
var i=0;

var b = {};

b.valueOf = function () {

    
    
      return ++i:
    

};

if (b==1 && b==2) {//success}

~~~
gkop

      var i=0;
      var b = {};
      b.valueOf = function () {
        return ++i;
      };
      if (b==1 && b==2) { console.log("success")}

------
yeahbutbut
And so far everyone has missed telling the poor OP how to avoid writing two
equality checks...

    
    
        if( [80, 443].indexOf(port) !== -1 && http === false )

~~~
skrebbel
Are you certain that that's an improvement?

~~~
yeahbutbut
For two options, almost certainly not ;-)

For N ports, or for a port list defined from a config file however it could
be. It's even nicer in python:

    
    
        ports = [80, 443, ...]
        if port in ports and http:
            ...
    

Or with underscore:

    
    
        var ports = [80, 443, ...];
        if(_(ports).contains(port) && http) {
            ...
        }
    

Good collections make it better than manually checking the return code or
checking an explicit set of port values. Also configuration > magic_nums.

------
gcr
This kind of abuse is also available in Python!

    
    
        In [3]:
        class TheObjectThatIsEqualToAnything(object):
            def __eq__(self, other):
                return True
    
        In [4]:
        x = TheObjectThatIsEqualToAnything()
    
        In [6]:
        x == 3 and x == 5
        Out [6]:
        True

~~~
Grue3
It's not necessarily abuse. I once had to code a dummy object that supported
all arithmetic operations with normal numbers and had a special comparison
logic that it is never equal to itself. Then you could test if something is a
number or a dummy object by comparing it to itself.

------
wambotron
I think the answer to "can something be fuzzy equal to one value and strictly
equal to another in JS?" has an easy answer that doesn't take much effort to
find. This might be cooler in another language, though.

var b = 2;

b == '2'; // true

b === 2; // true

~~~
neil_s
This is what I was gonna say! I don't know much JS, but after @sgdesign 's
recent primer, the answer to the first two conditions seems obvious, b=="2"
and b===2. Herpderp is just a cop out, since you could have infinite other
variables that b equals, if you set the value to the same thing!

~~~
philip_roberts
Except the question was b==1 && b===2

------
forrestthewoods
And here we all are reading this post during work hours. My god, the amount of
productivity lost because of that simple question in chat!

------
mathattack
Is this a case of readability versus theoretical perfection?

------
drvortex
0.25 = 0.25

1 - 3 + 2.25 = 4 - 6 + 2.25

1^2 + 2(1)(1.5) + (1.5)^2 = (2)^2 - 2(2)(1.5) + (1.5)^2

Since a^2 - 2ab + b^2 = (a - b)^2

(1-1.5)^2 = (2-1.5)^2

1 = 2

QED.

------
peterkelly
And now for exercise 2:

Port the solution to Haskell

~~~
chrra

      newtype X = X (IORef Int)
      instance Num X where
        fromInteger = X . unsafePerformIO . newIORef . fromInteger
      instance Eq X where
        (X a) == (X b) = unsafePerformIO $ do
                           x <- readIORef a
                           y <- readIORef b
                           writeIORef a $ x+1
                           return $ x == y
    
      ghci> a <- fmap X $ newIORef 1
      ghci> a == 1 && a == 2
      True
    

Edit: formatting.

------
cinitriqs
so...

(everything == all && all == everything) == stardust/code

------
Dewie
Defining a number to be a procedure (or 'word' in the parlance) is simple in
Forth.

> : a 2 ;

> : 2 3 ;

> a 2 =

> a 3 =

The two flags on the top of the stack are now equal.

------
ssdfsdf
This took you 11 days?!

~~~
poweribo
the classic feigned surprise

