println(m['one']) // ==> "1"
println(m['bad_key']) // ==> "0"
I think m['bad_key'] should panic rather than return a default value, because:
// TODO: implement a way to check if the key exists
Use optionals?
fn (m map[K]V) get(key K) ?V;
ob := m.get('bad_key')
assert ob == none
og := m.get('one')
assert og == 1
map[string]int{}
It's unfortunate that , could a map type just be e.g. map is a keyword, it clashes with functional map. Is a keyword necessary[string]int?
map is not a keyword:
https://vlang.io/docs#keywords
maps are going to behave like in Go:
m['bad_key'] == 0 (zero value)
or
val := m['bad_key'] or { println('not found'); return }
I also like Go's approach regarding panics: use them only in very exceptional cases. A missing key is a very common thing and should not result in a panic.
As for pointer types, not sure how to handle them yet. Maybe that's where a panic would be ok.
fn (m map[K]V) get(key K) ?V;
fn (m map[K]V) get(key K, default K) V;
?
BTW:
map is not a keyword:
V has 21 keywords:
What about pub?
What about pub?
That's the most recent one. Fixed.
@data-man:
fn (m map[K]V) get(key K, default K) V;
If https://github.com/vlang/v/issues/129#issuecomment-485017862 is implemented you can mimic your function easily with get returning an optional: m.get(key) or {default}. Knowing that a key isn't in a map is important.
map is not a keyword
OK, also pointer values for maps don't parse yet.
Panicking only when the map has pointer values is a special case that the programmer will likely forget, because the rest of the time indexing a map never panics. Making m[key] return an optional is safe but is less efficient, but compared to the cost of the lookup, an extra branch test is not much.
A missing key is a very common thing and should not result in a panic
It would be a programmer error (they should use m.get(unknown_match)); indexing an array out of bounds is another common programmer error, and that panics rather than returning a default value. Indexing a map is conceptually the same operation. This is important because generic code shouldn't care what type of container it is indexing, indexing should behave the same. Otherwise it gets messy with lots of extra $if tests.
val := m['bad_key'] or { 0 }
// or this (I'm not sure yet on `continue` semantics)
val := m['bad_key'] or { continue 0 }
Seems like the only consistent (with V) way to do this. The default or block could be a panic, but the block should allow for break, continue, return, or fallback value. The spec doesn't seem very clear on if/how an or block can provide a non-none value (continue 42?) but this would make sense. Just because an underlying fn has returned an error or empty value, it doesn't mean there is no reasonable way to continue with a default value provided by the caller.
Would that work?
val := m['bad_key'] or { val = 0 }
I think the fallback block should be an expression, not a statement.
Otherwise, how would you express the following:
doSomething(myMap['bad_key'] or { 0 })
// or
value * (scale[n] or { 1 })
Since Rust is one of V's inspirations, I think it is worth noting that getting a value by key from a map in Rust, returns an option type.
This is a _safer_ alternative to both panics and the alternative which is requiring all types to have some kind of unique bottom value (a la Go or nils), which is essentially what V appears to do now (empty string, 0, etc).
Bottom values either requires having NULLs for pointers and functions (like Go) or disallowing functions and pointers as map values (which would be a huge missed opportunity in expressiveness, in my opinion).
Just a further note: bottom values _feel_ nice for the simple scalar types because they can be interpreted as "identity" values which can be substituted for a real value. But they do not extend to all types (there is no meaningful "identity" pointer and although there is literally an identity function, it cannot stand in for any function). In other words, an "identity" value is operation/context sensitive.
This is true even for numbers:
1 + identity // identity must be 0
1 * identity // identity must be 1
Identity values as defaults only make sense in certain cases (e.g. in variadic operators in a Lisp: (* 2 3 4) ;=> 24, (* 2 3) ;=> 6, (* 2) ;=> 2, (*) ;=> 1, (+ 2 3 4) ;=> 9, (+ 2 3) ;=> 5, (+ 2) ;=> 2, (+) ;=> 0).
getting a value by key from a map in Rust, returns an option type.
The get function does but indexing does not:
assert_eq!(map[&37], "c");
https://doc.rust-lang.org/std/collections/struct.HashMap.html#impl-Index%3C%26%27_%20Q%3E
@bjeanes yes, map's get() will be returning an optional, and myMap['bad_key'] or { 0 } syntax will be supported as well.
@ntrel interesting, Go doesn't allow references to map values at all.
This issue has been closed due to the inactivity.
Please reopen this issue if you want to continue the discussion.
Most helpful comment
@bjeanes yes, map's
get()will be returning an optional, andmyMap['bad_key'] or { 0 }syntax will be supported as well.