3 ms·
Consider an expensive computation that may have no answer, and so it returns (after some heavy processing) "int|None". Also consider a cache lookup function th
by codebje 3y ago
Consider an expensive computation that may have no answer, and so it returns (after some heavy processing) "int|None".
Also consider a cache lookup function that takes some key and returns a generic T|None for T the type of cache entries, with None signifying the key was not found.
Both are, on their own, pretty reasonable things to have.
If you are using union types and you put the results of your expensive computation into your cache, you will not be able to tell when you've done an expensive computation for some key and got a None result, or when you haven't got a result in the cache for that key.
- kaba0 3y agoYou can have both union types and sum types (imo ideally), or in this case can easily choose to represent the result of the expensive computation in a more reasonable/descriptive way.
- codebje 3y agoPerhaps the more reasonable/descriptive way to represent the potentially no-value result of the computation is with a sum type, not a union type?
- deleted 3y ago[deleted]
- scubbo 3y ago> If you are using union types and you put the results of your expensive computation into your cache, you will not be able to tell when you've done an expensive computation for some key and got a None result, or when you haven't got a result in the cache for that key This is certainly an issue, but that's an issue for the _overall system_ of a cache which stores T. My claim wasn't that "Union types that Union with None can never cause issues", but rather that, for a function whose argument is `(int|None)|None`, there shouldn't ever be any different behaviour _of that function_ between "passed a T (int|None), which was None", and "passed a None (not a T)". The function itself should still behave the same way. You're right, of course, that _returning_ an (int|None), where "None" might mean "the answer is definitively known to be None" or might mean "the answer is unknown, and that is represented as None" can lead to unnecessary recomputation - but that's an issue of return types, not of parameter types.
- lmm 3y agoOne function's return value is another function's parameter. If the overall system has a bug, that's a problem, and I don't see any value in trying to quibble about whether it's a bug in the caller or the callee.
- scubbo 3y ago> I don't see any value in trying to quibble about whether it's a bug in the caller or the callee. Right, neither do I - like I said, "that's an issue for the _overall system_ of a cache which stores T.". The bug is that a given type (None) has different meanings to different components of the system, and this bug only arises _because_ "One function's return value is [being passed, directly, without any interpretation, as ] another function's parameter". If the type signatures were changed so that interpretation was required - so that "the answer is None" could be distinguished from "I don't have an answer" - then the bug in the overall system goes away.
- lmm 3y ago> this bug only arises _because_ "One function's return value is [being passed, directly, without any interpretation, as ] another function's parameter". Which is the normal way of programming. If you can't safely compose functions without adding an extra layer of interpretation between them, programming becomes much harder. > If the type signatures were changed so that interpretation was required - so that "the answer is None" could be distinguished from "I don't have an answer" - then the bug in the overall system goes away. Which is something that using sum types rather than union types achieves by default. Wherever you want to localise the problem, union types add a big, easy class of ways to shoot yourself in the foot that just aren't there if you use sum types.