Hacker News new | ask | show | jobs
by riffraff 1059 days ago
The article is claiming that the surface and the implementation are related.

If client code passed a null value before, an error would have occurred. Now it is handled with a default that the original code was not accounting for, and this might be bad.

Thus the change in interface _is_ a change in behavior.

3 comments

The caller could NOT pass a null value before.

Old situation: called function says: "i would crash if you gave me a null value, so my interface says you cannot give me a null value"

New situation: called function says: "i no longer crash if given a null value, which you couldn't do before anyway, so you won't notice any difference"

I believe this is exactly what Hickey was arguing.
But they could pass null, though. Clojure doesn't stop you from passing null anywhere.

Also, thrown exceptions are values. Consider for example a function that searches for a file a return null if it's not found, which you are composing with a function that opens a file but throws on null. You might very well write something like open(search(...)) and catching exceptions above that level. Now if I make open(..) able to accept null (maybe it opens a temp file?) then you now need to add a null check on the return value of search to get the old behaviour. That is 100% a breaking change!

> Clojure doesn't stop you from passing null anywhere

As a C++ programmer (oh, the horror!) I'd say that's an issue with Clojure rather than the general principle. And also one of the reasons I like the pointer/reference distinction in C++ (the former allows null values, the latter does not).

But sure, that changes my interpretation somewhat. It doesn't really change program safety, however.

But you already had to have a null check on on the return value of search if open wasn't previously able to take null, so the behaviour of your code hasn't changed.
Why? You may just have been catching the exception outside both functions. Lots of actual real-life programs, even if it's not the cleanest.
If the code passed in a null value before, it was violating the contract the function provided. A component isn't responsible for what happens when you operate it out of spec.

If I were a hardware designer and some changes to an IC I'm working on are going to make the next batch, say, work properly in hotter temperatures than it could before, and one of my coworkers comes and says, "Don't make that change, what if someone using that IC is deliberately operating it out of spec expecting it to fail and now the behaviour is going to change!" I'm going to start polishing up my resume, because I'm working for an organization that employs lunatics. Happily, that wouldn't happen in the hardware world; unhappily, I'm a software developer rather than a hardware designer.

Relevant XKCD, "keyboard heating":

https://xkcd.com/1172/

Which no client should/would have been using (given the error they would have received). Why would this new implementation be presumed incompatible, when any reasonable person would think this is an addition to the interface, and not a difference to the existing interface?
> no client should/would

No client could, even. He's talking about static types so it wouldn't have been possible even to express a caller not providing a value.