Nerdsniping: A glimpse into a stubborn mind(blog.andyet.com) |
Nerdsniping: A glimpse into a stubborn mind(blog.andyet.com) |
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.
Focusing solely on the means and disregarding the ends is a good way to not deliver anything useful. Most times, things need to be good enough, not technically perfect...
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.
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.
You get used to this as you get seasoned with javascript. Every language has it's quirks. The human brain is remarkably adaptable to using tools & dealing with intricacies. I personally utilize & appreciate the difference between == and === to reduce complexity in the code. I assume the reader also understands such differences.
I also find automated testing, logging, a module system like commonjs, and linters to be useful when programming in javascript. Once these are in place, systems written in javascript are remarkably maintainable & scale with complexity.
I spent seven years as a Perl programmer. Like Perl, a language can be extremely productive in the hands of a veteran and dangerous with a newcomer due to the unnecessary confusion it generates. At least eq and == are syntactically easier to grok than == and ===.
Except when I use JSLint. Talk about bringing you back to Earth when you think you've done something remarkable with JS.
The point is that side effects in code are dangerous, and can be used to mislead a reader.
If this happens, it is not the fault of JavaScript. Anyone who writes code like that for purposes other than demonstration or learning is a moron.
For example, it's not possible in Java or Objective-C to make an expression of the form a == b, where a and b are plain variables, have any side effects.
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++:
This makes it somewhat obvious that you can't change how basic types work in C++. On the other hand, JS's dynamicity makes for some interesting situations, as seen here. I wouldn't consider it "awful" but it's certainly a language with more "deep complexity" than appears at first glance.
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.
Fortunately, at this stage, we get the best of both worlds with syntax validators and linters. Abuse as appropriate (whatever that means) and be warned about the rest.
class B(object):
def __eq__(self, other):
return True
b=B()
It's much more expressiveThe language used can hardly be blamed for that.
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.
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.Couldn't you simply do this?:
if ((a==='80'||b==='443') && http===false) { ... }
if (a==='80' || (b==='443'&&http===false)) {...}
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.
You're comparing a literal number with an object, which are not the same type.
2 === new Number(2)
// => false
2 == new Number(2)
// => trueYou probably haven't. But a less experienced programmer has learned why you should always use ===.
#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
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 ...)".
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. a = {}
a.valueOf = function() {
var caller = arguments.callee.caller.toString();
//parse the caller function, and return
//different values based on what comparison is being made.
}
But I think this is even worse than the getter on the global object. var b = {c:0};
b.valueOf = function() { this.c++; return this.c; }
b == 1 && b == 2
> truevar b = {};
b.valueOf = function () {
return ++i:
};if (b==1 && b==2) {//success}
var i=0;
var b = {};
b.valueOf = function () {
return ++i;
};
if (b==1 && b==2) { console.log("success")} if( [80, 443].indexOf(port) !== -1 && http === false )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. In [3]:
class TheObjectThatIsEqualToAnything(object):
def __eq__(self, other):
return True
In [4]:
x = TheObjectThatIsEqualToAnything()
In [6]:
x == 3 and x == 5
Out [6]:
Truevar b = 2;
b == '2'; // true
b === 2; // true
(b == 1 && b===2 && b === herpderp)
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.
Port the solution to Haskell
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. let a = 1; _ == _ = True in a == 1 && a == 2(everything == all && all == everything) == stardust/code
> : a 2 ;
> : 2 3 ;
> a 2 =
> a 3 =
The two flags on the top of the stack are now equal.
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.
This perhaps illustrates my point. While you say == and === are equivalence relations (I would argue they are not), there is a wider problem.
= itself is an assignment operator and should never be considered as an equivalence relation. But it is normal to consider = as algebraic equality for anyone who has completed high school math.
It's a great loss to intuitive understanding that we use = for assignment. If anything, considering Javascript's == and === is a net benefit as programmers are more acutely aware of the issue here.
What percentage of time spent learning to program and recovering bugs is caused by overcoming this misintuition? It's surely a lot.
Wikipedia has further discussion: https://en.wikipedia.org/wiki/Assignment_%28computer_program...
If anything, mathematicians are incredibly used to overloaded equals operators.
These things are more of quirks of javascript than anything else. The post did nothing but made me hate javascript. Try doing the same in Java !
This isn't really a problem if b is enforced to be immutable.
This sentence means almost literally nothing, given how varied both "good enough" and "technically perfect" definitions are for each developer.
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.
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.
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.
if "get" returns different values when called multiple times within a single expression, one of two things must be true: * Someone's overriding "get" with a mutation to the variable. In all but the very rarest of circumstances, that someone should be publicly shamed. It's a nice hack for the purpose of the article, but not real code. * there's some very fine-grained concurrency. This case is irrelevant to the OP's claim because concurrency of this sort is very likely a fundamental characteristic of the system at hand, and never an artifact of the language.
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.
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. :)
Had I suspected the quality of my team, or my discipline would be under question, I would have left them in :)
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.
Both solutions work the same way - when accessing the value of "b" for the first check, b is mutated such that the next check will return a different value. Both of them are equally bad practices.
The == vs. === issue is the part that's actually irrelevant, it served as a red herring.
"This difference in how syntax is understood actually presents a barrier to programming for modestly trained mathematicians, who would otherwise be expected to excel."
> And are you implying that the author's original solution is more indicative of real programming?
Well, neither is... but yes, overriding == or .equals is somewhat common.
> when accessing the value of "b" for the first check, b is mutated such that the next check will return a different value.
The difference is that you could easily imagine a sane example where x == y != x === y, whereas it's hard to imagine a situation where I would want to override get (or ==) with a method that mutates the requested variable.
> The == vs. === issue is the part that's actually irrelevant, it served as a red herring.
For the claim re: syntax and compatibility with mathematician's expectation, it's exactly the other way around.
FWIW I feel the quoted claim re: syntax claim is interesting, whereas the contents of the article (summarily, "you can override == or .get and cause crazy stuff to happen") is unsurprising and kind of silly to most programmers even moderately experienced with OOP in dynamic languages (for the reasons given in this post and others -- everyone knows about it, it shouldn't take 11 days to figure it out, and in practice no one does purposefully obscure stuff like mutating values when == or .get is called).
edit: grammar