Scala Map.getOrElse - how to provide a default function

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> 

docs say the default should be of type: => B

If I understand correctly, the no-arg function returns Int in this case.

  • Why is the example taking mydefault1 compiled because it takes an argument and matches the specification?

  • Why are functions returned instead of functions called to get the default value? Apparently, the security types were violated because getOrElse should return an Int, not a function. (if I misunderstood the documents and incorrectly provided a function that required an Int value, why did the compiler let me provide a function instead of Int?).

change

Obviously:

  • extends the HashMap and overrides the default value or
  • using HashMap.withDefault

also allows you to use the function used to specify the default value. I would like that I could override the default value using the function provided at the time of the search (i.e., the Function may change during the life of the Map)

Is it possible?

+6
source share
1 answer

getOrElse definition :

 getOrElse[B1 >: B](key: A, default: => B1): B1 

The default argument does not accept a function that would be () => B1 , but a lazily accessible value of type B1 . This parameterless "function" => B1 also sometimes called thunk. The correct way to use:

 import collection.mutable val h = new mutable.HashMap[Long, Int]() def mydefault0(): Int = 101 println(h.getOrElse(99, default = mydefault0())) 

So what do you see with mydefault0 _ ? Obviously, the return value is of type B1 , which should be a common supertype of the type of the value of the map Int and the type of the default. The default value type is Function0 . If you assign a result, you will see that the supertype Any :

 val x = h.getOrElse(99, default = mydefault0 _ ) // x: Any = <function0> 

So the mistake is to suggest that you should pass a function when you are actually specifying an expression that is lazily evaluated. A call to mydefault0() will only be called if necessary by default. Formally, an argument is defined as a call-by-name argument.


Change Regarding your comment. Call-by-name means that you get a new array every 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!! 
+14
source

All Articles