Description
Scalac and Dotty both implicitly convert from function literals to PartialFunction
. For example, one can write:
val foo: PartialFunction[Int, String] = x => x.toString
However this conversion is different in Dotty and Scalac. The compilers need to generate an implementation for def isDefinedAt(x: A): Boolean
. Both compilers inspect the rhs of the function literal:
- Dotty: if the rhs is a
match
, implementsisDefinedAt
in term of thecase
s, otherwise returnstrue
- Scalac: if the rhs is a
match
, implementsisDefinedAt
in term of thecase
s, otherwise fails to compile
This leads to surprising behaviors:
val a: PartialFunction[Int, String] = x => x match { case 1 => x.toString }
a.isDefinedAt(2) // Dotty: false, Scalac: false
val b: PartialFunction[Int, String] = x => { x match { case 1 => x.toString } }
b.isDefinedAt(2) // Dotty: true, Scalac: false
val c: PartialFunction[Int, String] = x => { println("foo"); x match { case 1 => x.toString } }
c.isDefinedAt(2) // Dotty: true
// Scalac:
// error: type mismatch;
// found : Int => String
// required: PartialFunction[Int,String]
I would argue that a function literal is a partial function that is always defined and both Dotty and Scalac are wrong.
Note: It works like this in Dotty because the compiler desugars partial function literals into function literals:
{ case 1 => x.toString } // becomes x => x match { case 1 => x.toString }
Then when the compiler generates the isDefined
method, it doesn't differentiate between a tree that was originally a partial function literal and one that was a function literal