Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird type inference in scala

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?

like image 736
pmkrefeld Avatar asked Nov 22 '25 06:11

pmkrefeld


1 Answers

Original compile error

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.

Conversion to Unit

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.

like image 122
mikołak Avatar answered Nov 25 '25 00:11

mikołak



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!