The first value is the stored value in the hash table. If there is no key, the first value is NIL. The second value indicates whether the key is present in the hash table.
I list Common Lisp & its behavior in Appendix C at the end of the article. Multiple-return-value is definitely better than just returning NIL, but I still dislike it; it doesn't actively prevent you from confusing NIL with absence, so "the obvious way to do it" can still be the wrong way. A good CL programmer won't fall into this trap, but the article is in large part about how some languages/interfaces default you into good programming by requiring you to distinguish success from failure, and others don't.
Another way of seeing the same thing is: Type-theoretically speaking, MRV is using a product type (two values) to emulate a sum type (one value of two possible forms); why not just use a sum type?
Furthermore, I've found that absent keys degrading to nil is actually a very useful feature that makes a lot of code really composable and simple. I can't think of any Clojure code I've written recently where a value of nil actually should be distinguished from an absent value - I just write my code so that semantically they mean the same thing, because this makes the code much easier to compose and much easier to think about.
A more sane* way to implement the single lookup behavior is:
def values_in(keys, d):
ret = {}
for key in keys:
try:
ret[key] = d[key]
except KeyError:
pass
return ret
which works much more quickly in cases where keys is nearly all of d.keys, and doesn't force you to reinvent monads in Python, which is arguably unsuited for them.
* Sanity values may vary for people who haven't gotten into the habit of using Python exceptions for little things.
The article is looking at returning a list, while your option returns a dict, but that's an easy fix. And even for an Option-like approach, the article tries way too hard: Option can be viewed as a constrained cardinality list, and regular lists work just fine to implement them.
from itertools import chain
def getOpt(d, k):
return [d[key]] if key in d else []
def values_in(keys, d):
return list(chain(*[ getOpt(d,key)
for key in keys ]))
> In Lua, it's impossible to have a dictionary that maps a key to nil: setting a key to nil removes it from the dictionary!
Wait is he really trying to say, "Setting a VALUE to nil removes the entry from the dictionary!"? If so, that sounds terrible.
I can imagine barking up the wrong tree wondering why my map is missing fields when the real problem is an object I passed into it is unexpectedly nil.
Yes, that's true. It does have drawbacks, but overall I think it is better than say, JavaScript. In JavaScript, a key can be not present in an object, be undefined, be null, be set to some other value, or be present in the prototype. The difference between these can cause very subtle bugs.
//Let's define some object
var obj = {a: true, b: true, c: true, d: true, hasOwnProperty: true}
//Now let's make them falsy in different ways
obj.a = undefined; //set to undefined
obj.b = null; //set to null
obj.c = false; //set to false
delete obj.d; //delete key
delete obj.hasOwnProperty //delete key, but present in prototype
console.log(obj.a) //--> undefined
console.log(obj.b) //--> null
console.log(obj.c) //--> false
console.log(obj.d) //--> undefined
console.log(obj.hasOwnProperty) //--> [Function]
console.log('a' in obj) //--> true
console.log('b' in obj) //--> true
console.log('c' in obj) //--> true
console.log('d' in obj) //--> false
console.log('hasOwnProperty' in obj) //--> true
console.log(obj.a == null) //--> true
console.log(obj.b == null) //--> true
console.log(obj.c == null) //--> false
console.log(obj.d == null) //--> true
console.log(obj.hasOwnProperty == null) //--> false
for(var key in obj) {
console.log(key) //--> prints a, b, c
}
In Lua, the only data structure is a hashtable, which maps any non-nil value to any other non-nil value. It can also be set in the objects __index metamethod/table
local tab = {a = true, b = true, c = true}
-- by default there are none, but we can defined a fallback metatable similar to a prototype method like js's obj.prototype.hasOwnProperty
setmetatable(tab, {__index={c='meta c', d='meta d'}})
-- if it is important that a key is explicitly set to false, do so, otherwise simply remove it
tab.a = false --set to false
tab.b = nil --set to nil
tab.c = nil --set to nil, present in fallback table
tab.d = false --set to false, present in fallback table
print(tab.a) --false
print(tab.b) --nil
print(tab.c) --meta c
print(tab.d) --false
The rules for Lua are simple: Look for the key in the table, if it's not there, check the fallback defined in the metatable, if not, then return nil.
This is especially important in Lua, because any value can be a key, you would have to be sure to delete the key otherwise it could not be garbage collected.
local tab1 = {1,2,3,4,5}
local tab2 = {}
tab2[tab1] = true
for k,v in pairs(tab2) do
tab[k] = nil
end
If this worked like JavaScript, then tab2[tab1] would return nil, but tab1 could not be garbage collected until tab2 is. Because JavaScript objects can only have strings for keys, this is less of an issue.
Yes, that is what I meant. And yeah, it is weird behavior. (I think of "d[k] = v" as "setting the key k to the value v", so that's why I said "setting a key". Perhaps "binding a key" would be clearer?)
Null, empty string, empty value, Undefined etc. are the biggest causes of crashes,edge case bugs and a big time sink programming and data handling. Wish languages had better support to handle straightforward cases instead of just crashing.
Crashing is ok. The problem is that it usually doesn't crash in the right spot. A null is generated somewhere else and propagated around for a while and the crash happens later in a unrelated area. But it's even worse if it doesn't crash at all.
This is close to saying that lack of oxygen is the number one killer in drownings. That is, when you are in that situation there is a lot going wrong.
So, sure, you got rid of null from the language. Does your program handle the case where it was handed a negative number correctly? All positive values? All unicode?
Consider, in the Ariane Rocket incident, they were using a dependently typed language. Didn't magically prevent errors in assembly.
So, fine, we'll make our programs where they handle all cases... Rapid prototyping probably just flew out the window. As did worrying about actual honest to goodness use cases.
A great quote by Knuth I saw once was where he acknowledged that tex would choke on mile wide documents. But who cares?
[+] [-] lispm|11 years ago|reply
[+] [-] rntz|11 years ago|reply
Another way of seeing the same thing is: Type-theoretically speaking, MRV is using a product type (two values) to emulate a sum type (one value of two possible forms); why not just use a sum type?
[+] [-] derefr|11 years ago|reply
Further, in idiomatic Erlang, you don’t bother with a branch for each case of the option type; you just do the following:
Effectively, that's a type assertion: if maps:find/1 responds with error instead of {ok, _}, that’s a process crash.[+] [-] ataggart|11 years ago|reply
[+] [-] dkersten|11 years ago|reply
[+] [-] typomatic|11 years ago|reply
* Sanity values may vary for people who haven't gotten into the habit of using Python exceptions for little things.
[+] [-] dragonwriter|11 years ago|reply
[+] [-] anon4|11 years ago|reply
[+] [-] tieTYT|11 years ago|reply
Wait is he really trying to say, "Setting a VALUE to nil removes the entry from the dictionary!"? If so, that sounds terrible.
I can imagine barking up the wrong tree wondering why my map is missing fields when the real problem is an object I passed into it is unexpectedly nil.
[+] [-] russellsprouts|11 years ago|reply
This is especially important in Lua, because any value can be a key, you would have to be sure to delete the key otherwise it could not be garbage collected. local tab1 = {1,2,3,4,5} local tab2 = {} tab2[tab1] = true for k,v in pairs(tab2) do tab[k] = nil end
If this worked like JavaScript, then tab2[tab1] would return nil, but tab1 could not be garbage collected until tab2 is. Because JavaScript objects can only have strings for keys, this is less of an issue.
[+] [-] rntz|11 years ago|reply
[+] [-] unknown|11 years ago|reply
[deleted]
[+] [-] unknown|11 years ago|reply
[deleted]
[+] [-] th3iedkid|11 years ago|reply
http://www.oracle.com/technetwork/articles/java/java8-option...
[+] [-] TheCoelacanth|11 years ago|reply
[+] [-] wfjackson|11 years ago|reply
[+] [-] wvenable|11 years ago|reply
[+] [-] taeric|11 years ago|reply
So, sure, you got rid of null from the language. Does your program handle the case where it was handed a negative number correctly? All positive values? All unicode?
Consider, in the Ariane Rocket incident, they were using a dependently typed language. Didn't magically prevent errors in assembly.
So, fine, we'll make our programs where they handle all cases... Rapid prototyping probably just flew out the window. As did worrying about actual honest to goodness use cases.
A great quote by Knuth I saw once was where he acknowledged that tex would choke on mile wide documents. But who cares?