5 ms·
Nice article! It got me thinking about an issue I've noticed in dynamically typed languages (namely JavaScript) where it's very easy to end up doing lots of (po
by magical_h4x 7y ago
Nice article! It got me thinking about an issue I've noticed in dynamically typed languages (namely JavaScript) where it's very easy to end up doing lots of (potentially redundant) validation all over the place because it's much more difficult / unwieldy to pass that information along.
- undreren 7y agoYeah, the author mentions that too. For me, the funny thing about this article is that I kind of had the core insight just yesterday while hacking together a cli framework, but like the author, I couldn't quite explain what I learned.
- mamcx 7y agoYep. For example you call: config.load_from_file(.... And you can't know if it check if the file exist first or not. From pure habit you add checks... (what? looking at the code of the library? thats crazy!).
- makecheck 7y agoThe “exists” state is dynamic (something else in the system could be in the process of deleting the file since it was last checked). It’s more reliable if you don’t check in advance and simply try to open the file every time you need it. You handle errors while opening the file.
- stoops 7y agoThis is really interesting. For systems programming, files usually have clear ownership and modification context. If I call exists(), it should still exists for as long as that thread is paused. It's an anti-pattern to throw errors for expected behavior.
- Izkata 7y agoThe idea appears to me to have been popularized by python, where it's straight called an antipattern to do the check beforehand [0]. The language also uses exceptions for standard control flow - for example, there's one called StopIteration that the language uses to indicate to loops that the current generator or iterator has reached its end. [0] https://docs.quantifiedcode.com/python-anti-patterns/readability/asking_for_permission_instead_of_forgiveness_when_working_with_files.html https://docs.quantifiedcode.com/python-anti-patterns/readabi...
- Sharlin 7y agoWhich is why you don't throw errors but return a result type such as Rust's Result, Haskell's Either, or C++'s upcoming std::expected :) On the other hand, non-atomically asking for permission first in any concurrent (or re-entrant!) context is not just an antipattern but simply wrong and always a possible race condition. It is impossible to write a routine that tries to open a file and is statically guaranteed to succeed.
- ricardobeat 7y agoIn this case not even TypeScript can rescue you. You might naively implement: const head = (input: A[]) => input[0] and it will happily infer a return type of A. Then you'll fall flat on your face the first time you encounter an empty array: head([]) // -> undefined To make it correct you need to explicitly define a return type of (input: A[]): A | undefined, just as the Maybe. It's obviously impossible for TS to guarantee that an array will not be empty at runtime, but I wish this specific case triggered some help from the type checker.
- tylerhou 7y ago> It's obviously impossible for TS to guarantee that an array will not be empty at runtime, but I wish this specific case triggered some help from the type checker. type NonEmptyArray<T> = { head: T, rest: T[] }; function makeNonEmptyArray<T>(array: T[]): NonEmptyArray<T> | null { if (array.length == 0) return null; return { head: array[0], rest: array.slice(1, array.length) }; } function head<T>(array: T[]): T | null; function head<T>(array: NonEmptyArray<T>): T; function head(array: any): any { if (Array.isArray(array)) { if (array.length === 0) return null; return array[0]; } if (typeof array === "object") { return array.head; } } http://www.typescriptlang.org/play/#code/C4TwDgpgBAcg9gOwKIFsygIICcsEMQA8AKgHxQC8UA3lABYS4AmAXFEQDRRYQDOwrRANoBdKAF8A3ACgpAMwCuCAMbAAloigpcAawjxkaTDnzESAClzGQAkQEpW+1OhDY8hUlAA+UBPIA2ftRSUFCqslAWVgB0fhAIAObAtBSUAAy2XBDA8lgIPv5+0iHc2bnUdAwsUJZugqnCnNx8rDX4UTx+qkoQZgCMnK0gMXGJtBmSUmIyCspqGvRMppFuNsL2bF75AdIzKup5C4xLgw6ITkZuputEO4p785XL+C0IIOu4r0EhYRGubao8P4gJ5vDJUYIhULhEHDBJJFJpDIlHJ5XzbCHFLIo6pWOrCIriGTfaGgSBwcKDBFQABEcAARgArCAqalgjGZUp5QZRQ4EqZiIA http://www.typescriptlang.org/play/#code/C4TwDgpgBAcg9gOwKIF... It's impossible for Haskell to guarantee an array will not be empty at runtime as well; that's why we can write a new type + a smart constructor to track runtime properties.
- s_severus 7y agoA possibly more elegant TypeScript solution: type NonEmpty<T> = [T, ...T[]]; function head<T>(input: NonEmpty<T>): T { return input[0]; } https://www.typescriptlang.org/play/?ssl=5&ssc=2&pln=1&pc=1#code/C4TwDgpgBAcg9gOwKIFsygDwBUB8UC8UA2lgDRQB0VWRAurQNwCwAUKwGYCuCAxsAJaIoACwgBDACbYcACn4IwnYAC5YiVOhDSAlKqxQA3qyhQAThGCdTCKPMXAiABkasAvq1ajJMutuYsAegCoAEFTAHNOFAgEYCg4dihQSCgAcjpU2wBnKAQ4OLEsrP5whDEAIwAbaGA4KDAxUzFo4AhTeMTk6FT4ZDRMbgBrPIB3BBxUilYgkwAFUzhIU1A0x0z+HJQN4oRw2xsutIyocqUzCABHTn5zCX2k8G7ejQGEYbgxiam2Fi8JHwATLQ-FAZnBBgBCDwsHiILJxBBRcptLKqREoZGmOgEYgARhcv3E-3RmKyfmmwTCkWisQ6DxSqRJbWOG1y+SghWKpQq1SSdQaTRabTphx66n6WiZpi+0NhCHhbL6mhgSJRqmeEowUrwhCI+P8fxkeSVoBVGJRILBkKAA https://www.typescriptlang.org/play/?ssl=5&ssc=2&pln=1&pc=1#...
- dudul 7y agoEverybody notices this. That's why most dynamically typed languages, after having reached good adoption, try to shoehorn type safety to fix that.
- BurningFrog 7y agoTo me, unit tests replaces almost all I got from type safety. And that's just a side effect even!
- NieDzejkob 7y agoTests can only prove the presence of bugs, not their absence.
- BurningFrog 7y agoThat's also true of type safety, and every other technique...
- NieDzejkob 7y agoNot if you take into account the bug classes the technique intends to eliminate.