Why MD5('240610708') is equal to MD5('QNKCDZO')?
stackoverflow.com
stackoverflow.com
> let a = "0e462097431906509019562988736854"; // = md5("240610708")
> let b = "0e830400451993494058024219903391"; // = md5("QNKCDZO")
> let c = 0;
> a == c
true
> b == c
true
but > a == b
false
(Of course, also solved by using ===)For a one-week language, it's pretty darned good! But it was still a one-week language. The behavior of the == operator is one of the places where that really shows. I'm sure Brendan Eich would have cleaned that up with more time, but there wasn't any. And the behavior of the == operator is just one of those things that simply can not be fixed for backwards compatibility reasons. That's why an additional one had to be added later that works more sensibly.
There's a few other old corners of JS that have survived into the modern era. See also the excitement of hasOwnProperty and all that involves. This was a huge mess for a long time.
The work on cleaning it up, against the requirements of backwards compatibility and the uphill battle of having so much of it deployed out in the world, has been exemplary. But there is still some things that just can't have much done about them, == being one of them.
The reason for the deadline wasn't that Java was a good idea. You should also bear in mind we're not talking the Java of today, but the Java of not-even-1.0 yet. Java in 1995 wasn't a very good language. Sun was just jamming it everywhere they could, and the browser looked like an appealing target from a marketing point of view. From a technical point of view it was insane.
Java grew up, certainly. But it was forced on the world on the back of an enormous marketing budget and a full court press, not the technical merits of Java circa 1995. Many people around here remember the XML push forcing a technology everywhere, even where it doesn't belong, well, the one before that was Java. Completely inorganic.
One of the inspirations for Java was the language "Bob" by David Betz
Even it would have been a win over JS.
@Article{Betz:1991:YOT,
author = "David Betz",
title = "Your own tiny object-oriented language",
journal = j-DDJ,
volume = "16",
type = "PL",
number = "9",
pages = "26, 28, 30, 32--33, 86, 88--89",
month = sep,
year = "1991",
CODEN = "DDJOEB",
ISSN = "1044-789X",
bibdate = "Tue Sep 10 09:11:02 MDT 1996",
bibsource = "http://www.ddj.com/index/author/index.htm;
http://www.math.utah.edu/pub/tex/bib/dr-dobbs-1990.bib",
note = "Reprinted in \cite{Betz:1994:YOT}.",
acknowledgement = ack-nhfb,
classification = "C6140D (High level languages); C6150C (Compilers,
interpreters and other processors)",
keywords = "Bob; C++; C-like syntax; Class system; Interpreter;
Lisp; Tiny object-oriented language",
thesaurus = "C listings; High level languages; Object-oriented
programming; Program interpreters",
}
http://www.txbobsc.com/misc/bob-article/bob-article.htmlIt shouldn't be excused. Every feature is the potential for an equal or larger detractor.
As long as conversion between representations is well-defined and not hugely surprising, what does it matter?
Surely the MD5 example demonstrates exactly how this ends up being a problem.
On the surface "1e5" == 100000 seems like a reasonable conversion between representations and unsurprising, but by silently converting things and then comparing the result we get this astonishing result.
Conversions need to be made explicit. This is an opportunity for programmers to consider whether they actually wanted a conversion (the MD5 programmer did not) and for API designers to offer multiple distinct conversions when appropriate. "1e5" == 100000 is only one reasonable interpretation. Maybe it's a lowercase hexadecimal value. If you make the programmer explicitly convert they can choose.
Coercing an explicit string e.g. "1e5" to a numeric is boneheaded. It's fine to support different numeric formats like exponent or hex format. Weak typing and type coercion is a major footgun in a language.
Type coercion is an expression of strong typing, and it's inevitable if you want a language that's usable.
For example, "array of int of length 5" is a type, and "array of int of length 6" is a different type. Do you want to write a separate sort function for each of those types?
Because Default is tricky to genericise, there literally are 32 distinct implementations provided for Rust's [T; 1] through [T; 32] of Default::default()
In Javascript:
let a = '0';
a == !a; // True. Why?
let b = !a;
(a == b) == (!a == !b) // False. Why?
None of the coercions involved is particularly surprising, but the result most definitely is.I think there are two valid options: false and signaling an error (by any means such as returning null, throwing, etc). JavaScript chooses the third (for some strings)
Here's Rust for example: error[E0277]: can't compare `&str` with `{integer}`
[The full diagnostic points to where exactly your code tries to perform such a comparison and explains that you could do this if PartialEq<{integer}> was implemented for &str, which it is not, and then suggests things which are PartialEq for &str, such as other strings and various string-like objects a sane person might compare them to]
But I know I appreciate it when I can query my database with:
SELECT * FROM orders WHERE delivery_day
between '2023-04-10' and '2023-04-12'
Instead of using SELECT * FROM orders WHERE delivery_day
between PARSE_DATE('%Y-%m-%d', '2023-04-10')
and PARSE_DATE('%Y-%m-%d', '2023-04-12')
So I can see why language designers put in some implicit conversions.In general == should not coerce types automatically. Automatic type coercion, even in dynamic languages, is the worst aspect of dynamic typing.
For anybody who cares: `a == c` coerces `a` into a number . `a` is a hexadecimal string that happens to have only one non-numeric 'digit', and that digit happens to be an 'e'. This triggers parsing as scientific notation. For both `a` and `b`, the only digit in the significand is 0, so the result is 0.
Edit: I'm an idiot, thanks for the responses for correcting me, was thinking PHP but had Python on the brain I guess. Original bad edit: I was downvoted pretty hard, but I think I was just unclear. In Python, doing md5("foo") == md5("bar") returns true because Python converts both strings to numbers first because they have an e in them. That is, in my opinion, Python is doing an unnecessary conversion there that JS doesn't do in that case. See https://stackoverflow.com/questions/12598407/why-does-php-co...
>>> 1 == True
TrueBetween it being an ancient footgun and the ubiquity of linters, making fun of "==" code ends up just making fun of beginners.
I do try to use the newer language features like nullish-coalescing and optional chaining to make the check less necessary, but sometimes it makes more sense (clearer code, less unnecessary code execution) to check for null/undefined and return early.
Just because of that I've created an `isNil = (x) => x is undefined or null` function and banished == from my code.
== can lead to what we've jokingly called tollbooth code: code that can make every reader stop and pay a mental toll while they confirm that the writer did intend to write the code that way.
It never occurred to me that it could be so noticeable to others as to add any cognitive overhead. I’m not discounting that at all, to be clear, only that it’s not something I’d ever considered before now.
* Which doesn't imply it's not useful.
Other language design options are to use pointers / references, be strongly typed or be functional. Seems like you're saying all weakly-typed object-oriented languages are failures?
And I don't speak PHP.
Anyway yeah being able to compare identity and equivalence separately seems important.
You can argue that triple equals is a confusing syntax, but the underlying problem is inherent in the world we live in.
To me this is the problem with triple equals. The different types of equality should be explicit in their use rather than overloading a sigil.
Perl does better here by requiring "eq" for strings, so there's less chance for such an issue to happen. But you have the "zero but true" fun quirk -- "0e0" evaluates to 0 numerically, but 1 logically because it's not an empty string.
Weak typing is when a language inserts implicit coercions in unexpected places, such as 42 == “42”. It’s not a precisely defined term. Another example is in C where pointers and values may be mixed and matched, although less so nowadays.
The way I see it, this was a crude attempt to make things like $_GET['age'] > 18 work without having to remember to convert types manually.
Javascript was the same with input.value
The == conversions become more intuitive if you keep this use case in mind and think about semantic equality instead of programming's more traditional exact equality.
I remember trying to help a friend with their PHP assignment in college (~2001). I had only ever worked with C, C++, and java in my classes and I just remember being was confused as hell since I couldn't understand why PHP worked the way it did.