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.
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!