4 ms·
The problem is that ‘get’ possibly creates one nil result. Using the map as a function, even if nil is understood to be an error, now gives you two possible and
by hellofunk 8y ago
The problem is that ‘get’ possibly creates one nil result. Using the map as a function, even if nil is understood to be an error, now gives you two possible and very different errors from one expression. It’s a bad idea and generates added ambiguity and complexity, which further strays from the article’s philosophy.
- masklinn 8y agoThere is no ambiguity to using map as a function. It raises an error if the map is nil, and returns a nil if the key is not found. Using get is ambiguous, getting a nil result may mean that the key was not found or that there was no map in the first place.
- fiddlerwoaroof 8y agoYeah, Common Lisp’s gethash is better than get in most other languages because of Common Lisp’s support for multiple return values. (gethash key hash-table) Returns two values: the first one is the value found in the hash table, or nil if no value was found. The second one is nil (i.e. false) if no value was found and non-nil (maybe t? I forget what the standard says) if a value was found.
- masklinn 8y agoIt's the style used by Go and certainly better than Java/Ruby/JS (which just return null/nil/undefined on a missing key, same as clojure). My personal ranking would be along the lines of: 1. Option types, that only makes sense for statically typed languages but it's the most clear and helpful and all other styles can trivially be composed from that. 2. Erlang-style, that's less the API which is similar to (though less forgiving/error-prone than) CL/Go and more the language: find/2 returns `{ok, Value} | error`, so to actually get the value you must either assert that the retrieval succeeded by pattern-matching on `{ok, Value}` directly (faulting if that didn't work) or properly pattern-match on both cases, and it doesn't give the illusion of a value on failure. Erlang also provides (3) and (5) via get/2 and get/3. 3. Python-style, the default is to raise on missing keys which makes it unmissable that the value was not there, alternatively provides a variant of (5) which can fairly easily be used as a (4) via the get method (return a placeholder value which can't have existed in the map, check against it by identity). 4. Common Lisp & Go, both will implicitly ignore the presence flag by default (bind result to single value) and just return a default on miss in that case. Common Lisp has an edge because Go's statically typed so it could make better use of MRV, and CL has much better abilities to manipulate and reify MRVs (only thing you can do in Go is bind them). 5. Clojure, Ruby and (finally) Java >= 8: a missing key returns a valid default[0], but it's also possible to customise that default (oddly enough in Ruby the same method which lets you get a non-hash-global default also lets you opt into (3), but I don't think it's used much?). For Clojure and Ruby this lets you emulate a (4) the same way Python does. 6. Java < 8/JS: a missing key returns a valid default (null/undefined) and there's no way to differentiate "there was no value" and "this was a value" save by a second access (in/contains). [0] as in the default they return could have been a valid value all along
- fiddlerwoaroof 8y agoI really like Common Lisp's solution because it doesn't force boilerplate on you. If you care whether or not the key was present, the second return value is there and, otherwise, you just continue on your way. Also, since MRVs are a language construct, you can abstract over them to implement the equivalent of Haskell's >>= / maybe or to throw on a missing key. So, I'm not really sure there's a decisive advantage of Options vs. the MRV solution. In fact, if you change your perspective a bit, Options (and possibly sum types in general) are just ways of getting multiple return values in languages that only support single return values: i.e. you could think of the case of the sum type (i.e. "Some" or "None" being one of the return values and the wrapped value(s) being another.
- masklinn 8y ago> I really like Common Lisp's solution because it doesn't force boilerplate on you. Checking what needs to be checked is not boilerplate. > If you care whether or not the key was present, the second return value is there and, otherwise, you just continue on your way. Which is the entire issue. By default, if you're not careful or don't check, you just get garbage without warning. > Also, since MRVs are a language construct, you can abstract over them No, MRV being a language construct does not allow that at all (you can't do any such thing in Go because MRVs are language structures rather than properly reified). You can abstract over MRVs in CL despite them being MRVs, because CL specifically provides a bunch of dedicated constructs for working with MRVs. > So, I'm not really sure there's a decisive advantage of Options vs. the MRV solution. That options force the developer to deal with the issue and do so by default. > In fact, if you change your perspective a bit, Options (and possibly sum types in general) are just ways of getting multiple return values in languages that only support single return values Utter nonsense, MRVs are products. The entire point of sum types is that they're not, their purpose is to provide alternatives in a type-safe manner. The way to get multiple return values in "languages that only support single return values" is tuples. > you could think of the case of the sum type (i.e. "Some" or "None" being one of the return values and the wrapped value(s) being another. You really could not, that's the entire point.
- 8y ago