Reputation: 15800
Odd...
val h = new HashMap[Long, Int]()
def mydefault0():Int = 101
println( h.getOrElse(99, default=mydefault0 _ ) ) // Prints <function0>
def mydefault1(key:Long):Int = 102
println( h.getOrElse(98, default=mydefault1 _ ) ) // Prints <function1>
The docs say that default must be of type: => B
If I understand correctly, a no-arg function returning an Int, in this case.
Why does the example taking mydefault1 compile, as it takes an argument and so does meet the specification?
Why are functions returned, instead of functions being called to yield a default value? Apparently the typesafety has been broken because the getOrElse must return an Int, not a function. (if I misunderstood the docs and incorrectly provided a function where an Int value was required, why did the compiler let me provide a function, instead of an Int?).
edit
Clearly:
also let a function by used to specify a default value. What I'd like is to be able to override the default with a function that's provided at the point of performing the lookup (i.e. the function may change during the life of the Map)
Is this possible?
Upvotes: 7
Views: 10559
Reputation: 67310
getOrElse[B1 >: B](key: A, default: => B1): B1
The default
argument does not take a function—which would be () => B1
, but a lazily accessed value of type B1
. This parameterless "function" => B1
is also sometimes called thunk. The correct way to use it is as follows:
import collection.mutable
val h = new mutable.HashMap[Long, Int]()
def mydefault0(): Int = 101
println(h.getOrElse(99, default = mydefault0()))
So what is it that you are seeing with mydefault0 _
? Clearly, the returned value has type B1
which must be a common supertype of the map's value type Int
and the default value's type. The default value's type is Function0
. If you assign the result, you see that the supertype is Any
:
val x = h.getOrElse(99, default = mydefault0 _ ) // x: Any = <function0>
So the mistake is to assume that you must pass in a function, when actually you are stating an expression which is lazily evaluated. The call mydefault0()
will only be invoked when the default value is needed. Formally, the argument is defined as a call-by-name argument.
Edit: Regarding your comment. Call-by-name means that you do get a new array each time.
val m = Map("foo" -> Array(1, 2, 3))
def myDefault = {
println("called-default")
Array(4, 5, 6)
}
val a1 = m.getOrElse("foo", myDefault) // myDefault not called
val a2 = m.getOrElse("bar", myDefault) // myDefault called
val a3 = m.getOrElse("baz", myDefault) // myDefault called
a2 == a3 // false!!
Upvotes: 17