I have some trouble with Scala type inference. In following worksheet example I have defined a Map which maps Any values to a function returning a Unit value.
Interestingly, when I try to define the same map using only one line of code it doesn't work because the 'bar' functions return-type suddenly changes to Any instead of Unit.
type UnitFun = (Any) => Unit
val foo = "foo"
val bar = (a: Any) => System.out.println("bar")
val map: Map[Any, UnitFun] = Map().withDefaultValue(((a: Any) => Unit))
val doesCompile: Map[Any, UnitFun] = map + (foo -> bar)
val doesNotCompile: Map[Any, UnitFun] = Map().withDefaultValue(((a: Any) => Unit)) + (foo -> bar)
I am using IDEA14 as IDE with Scala 2.11.6
It seems to me like this is a Feature/Bug of the Scala compiler, or am I missing something?
btw I've just noticed that when I use 'bar' as default value in 'doesNotCompile' like this:
val doesCompileNow: Map[Any, UnitFun] = Map().withDefaultValue(bar) + (foo -> bar)
it suddenly seems to work, I am pretty baffled right now. :D
Edit 1: @Mikolak
In this case, how does the following code works? :)
val a: Any => Unit = (a: Any) => Unit
val b: Any => Unit = (a: Any) => ()
Shouldn't both expressions be of a different type? Or is there some implicit type conversion involved?
The compile error happens because this function:
(a: Any) => Unit
is of type
Any => Unit.type
and not of type:
Any => Unit
In other words, you are returning Unit, the companion object of the Unit type. That companion object has a type Unit.type, which is a different type than Unit (this applies to all companion objects in Scala).
What you need is to actually return a value of type Unit. As outlined in the docs, the only such value is ().
So, your default function should be: (a: Any) => ().
EDIT: Regarding the supplementary question.
Here:
val a: Any => Unit = (a: Any) => Unit
you are explicitly typing the expression to have a return type of Unit. Normally it would result in a type error, but (as you suspect) "fortunately" you triggered one of the pre-defined implicit value conversions, specifically :
Value Discarding
If e has some value type and the expected type is Unit, e is converted to the expected type by embedding it in the term { e; () }.
So, this:
(a: Any) => Unit //return type is Unit.type
becomes:
(a: Any) => {Unit; ();} //return type is Unit
Note that as per the definition, the conversion applies to any value, so e.g. val c: Unit = "c" produces the same result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With