5 ms·
> Extending a function to accept null, under dynamic typing, can break a program which depends on the documented behavior of that function having thrown an exce
by fiddlerwoaroof 3y ago
> Extending a function to accept null, under dynamic typing, can break a program which depends on the documented behavior of that function having thrown an exception for that case.
This just means that the preconditions are “the value is not null” even if there are no types to capture that precondition. I was pretty careful not to use the word “type” in the comment you’re replying to.
- kazinator 3y agoThe problem is that in fact there are no preconditions; the function can be called unconditionally using any value as an argument. It throws for some values, and any change in which ones is a visible change. Programmers will make code dependent on anything that is visible in an API, even if the specification tells them not to, an ISO standard tells them not to and even if their mother tells them not to. Here, the spec may even be saying: if you call this function with a non-string, it throws an exception.
- fiddlerwoaroof 3y ago> The problem is that in fact there are no preconditions; the function can be called unconditionally using any value as an argument. There are no _checked_ preconditions. It’s only meaningful to talk about the correctness of a program relative to a specification of its behavior and a specification needs to specify preconditions and postconditions. So, if there are no preconditions, any behavior of the function being called is as correct as any other behavior. The fact that some programming languages allows the callers of a function to pass values that don’t satisfy the spec is irrelevant to the point I’m making.
- kazinator 3y agoThe function checks for a type with assert: (assert (stringp arg)). But that check is a documented behavior which generates a predictable exception that the caller can detect and so can tell when that is suddenly missing. No matter what consequence you give to the failed check, something can break when the check is taken away. We could have a True(r) Scotsman's checked precondition like this (unless (stringp arg) (abort)) ;; bail the entire process image with abnormal status If we check it like that, nobody will try to depend on the behavior within an application, in the way that my test program did. It can still be a breaking change if the function is written that way, and then changed so the abort is taken away. Because the application can do this: (defun my-stupid-abort () (strlen nil)) ;; this calls abort! So now if the program relies on (my-stupid-abort) to abort, that will break; suddenly that function call returns zero and the process keeps going. You really need: ;; ... ffi definitions here ... (unless (stringp arg) (acpi-power-off)) But again, dumb program can depend on (strlen nil) to power of the machine, which could be part of some critical system that breaks as a result of it not powering off. Only a compile time check can do it, by preventing the program from existing. A program that never compiled and therefore was never deployed to its installation will never break.
- kazinator 3y ago> So, if there are no preconditions, any behavior of the function being called is as correct as any other behavior. Umm, no. If the program is supposed to unconditionally produce the output "Hello", but it instead produces "Goodbye", then it is incorrect. Unconditionally means not that there are no preconditions but that the precondition is T (true). If the precondition is constant truth, it means that there are no circumstances under which the program can be excused for not satisfying the postcondition (e.g. producing "Goodbye" rather than "Hello"). Correctness is almost irrelevant here, because the question is about breakage. It is in fact the change in specification which is behind the breakage. The function is correctly implementing its specification at all times; the correctness of my example strlen is never being called into question. What it means for it to be correct has been changed, and the function's definition followed suit.
- fiddlerwoaroof 3y agoA breaking change is one that breaks code that uses it correctly. This means that the code that uses it uses it according to its specification. Breaking code that uses your code incorrectly should happen early and often because using code incorrectly is a bug in the using code, not the code being used.
- kazinator 3y agoThat's just a blame game that is neither here nor there to the user who can't open his ten-year-old word processing document with the latest version.
- fiddlerwoaroof 3y agoIt makes all the difference in the world for that problem. It’s how you build programs that can be maintained and evolved in a consistent fashion long-term.