▲ | quotemstr 10 hours ago | |
> you could pass a function literally anything and it was on the implementation to deal with it So? Language is irrelevant here. What matter is the contract, the agreement that operates through time and space between caller and callee on behavior and expectations. Contracts exist outside of the type system. All a type system can do is enforce a subset of the contractual requirements. For example, log(0) is undefined. If you write a function to compute log(x), you can (and should) document the function as having unspecified behavior when x is zero. That's the contract. The contract holds whether the function is spelled like this
or
In the second example, we have the type system enforce the "is a number" rule, but at least in TypeScript, you can't use the type system to check that x is positive. In some languages you can, but even in those languages, programs have invariants the type system can't express.Likewise, you could define a version of log that accepted, e.g. spelled numbers, like log("three"). You can express this weird contract with static or dynamic typing. The nasty thing is that if early in the log library's history, before the author wrote down the contract or even had TypeScript, someone had written log("three") and gotten a type error, the log library's author might have "fixed" the "bug" by checking for spelled numbers as inputs. That would have been an expansion of the function's contract. The log library author probably wasn't thinking about the long-term downsides of this expansion. He could have, even when our log() was a pure-JavaScript, just said "The contract is the input is a positive number. Your code is buggy." But our hypothetical log library author didn't have the experience or good judgement to say no, so we ended up with the kind of "corner case bloat" the article's author describes. |