4 ms·
But what if the map itself is nil? That’s why using maps as functions is generally not done in the wild.
by hellofunk 8y ago
But what if the map itself is nil? That’s why using maps as functions is generally not done in the wild.
- masklinn 8y agoThat's nil punning, which the article specifically does not care for if not outright decries in its conclusion: > Over at Lispcast, Eric Normand argues for the “nil-punning” approach, which is fine. But I think this approach requires a confused notion of what nil/Nothing actually means. […] It is much simpler to understand nil as Nothing, i.e. the absence of a value (which is a type). Assuming that, the behaviour of `get` is specifically one you do not want. Furthermore, calling the map actually behaves as "Haskell and other ML-ish languages". `Data.Map.lookup` takes a Map, not a Maybe Map.
- hellofunk 8y ago‘get’ is a function that will always exist, but using a map in function position is very fragile and everything breaks if the map itself is nil. If you advocating for this, it is contrary to general consensus in the community; you will almost never see this in real code. Responding to your edit: it’s safe to do it in Haskell because you know it isn’t nil at compile time.
- masklinn 8y ago> ‘get’ is a function that will always exist, but using a map in function position is very fragile and everything breaks if the map itself is nil. It's no more fragile than any other function which doesn't do nil punning, and again the essay we're supposedly discussing advocates not using nil punning.
- hellofunk 8y agoBut you are advocating to use the map in function position? That’s much worse than get, most of the time.
- masklinn 8y agoAll I'm pointing out is that for the purpose of the article there seems to be no difference between using the map as a function and using get, and in fact that using the map directly is more suitable to the essay's espoused philosophy of avoiding nil punning and treating nil as a hole/error rather than a normal value.
- hellofunk 8y agoThe 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