A and (=> A) do not unify when used as function type parameters
It doesn't seem possible to use (=> A) => B in contexts requiring A => B:
scala> def log(a: => Any): Unit = println(a)
log: (a: => Any)Unit
scala> def apply[A, B](a: A)(f: A => B) = f(a)
apply: [A, B](a: A)(f: A => B)B
scala> apply("hello")(log)
<console>:10: error: type mismatch;
found : (=> Any) => Unit
required: Int => ?
apply("hello")(log)
^
If you try to go the other direction, using A => B in contexts requiring (=> A) => B, the result depends on how you're trying to do it:
scala> def log(a: Any) = println(a)
log: (a: Any)Unit
scala> def apply[A, B](a: A)(f: (=> A) => B) = f(a)
apply: [A, B](a: A)(f: (=> A) => B)B
scala> apply("hello")(log)
<function0>
scala> apply("hello")(identity)
java.lang.ClassCastException: $anonfun$apply$1 cannot be cast to java.lang.String
Imported From: https://issues.scala-lang.org/browse/SI-7431?orig=1 Reporter: Dan Rosen (mergeconflict) Blocks #7430
@paulp said: => is underspecified so I'm guessing, but to use (=> A) => B where A => B is expected would imply either that A <:< (=> A), which can't be the case, or that there is something which automatically converts values of type (=> A) => B to values of type A => B, which there isn't unless you make one.
But you can make one.
scala> implicit def upgradeByName[A, B](x: (=> A) => B): A => B = (p: A) => x(p)
upgradeByName: [A, B](x: (=> A) => B)A => B
scala> apply("hello")(log)
hello
The other way is buggy as is clear from the CCE.
Dan Rosen (mergeconflict) said:
Yeah... I'd assumed that the value type A and parameterless method type => A were compatible or equivalent. The only thing in the spec that vaguely justifies that assumption is:
6.26.2: A parameterless method m of type => T is always converted to type T by evaluating the expression to which m is bound.
This is talking about the semantics of expression evaluation, though, not the typing rules...
@paulp said: The key word is "converted".
Maybe this hasn't been fixed yet because it both should and should not compile.
I hear Kentucky Mule can do both in parallel.
The situation doesn't seem to have changed in Scala 3. Even the ClassCastException remains reproducible.
Welcome to Scala 3.2.1-RC1-bin-SNAPSHOT-git-f0be00d (18.0.1.1, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> def log(a: Any) = println(a)
def log(a: Any): Unit
scala> def apply[A, B](a: A)(f: (=> A) => B) = f(a)
def apply[A, B](a: A)(f: (=> A) => B): B
scala> apply("hello")(log)
rs$line$2$$$Lambda$1434/0x00000008010b09c0@76e9eed8
scala> apply("hello")(identity)
val res0: String = hello
scala> def log(a: => Any): Unit = println(a)
def log(a: => Any): Unit
scala> def apply[A, B](a: A)(f: A => B) = f(a)
def apply[A, B](a: A)(f: A => B): B
scala> apply("hello")(log)
-- [E007] Type Mismatch Error: -----------------------------------------------------------------------------------------
1 |apply("hello")(log)
| ^^^
| Found: (=> Any) => Unit
| Required: String => Unit
|
| longer explanation available when compiling with `-explain`
1 error found
scala> apply("hello")(log(_))
hello
or
Welcome to Scala 2.13.8 (OpenJDK 64-Bit Server VM, Java 18.0.1.1).
Type in expressions for evaluation. Or try :help.
scala> def log(a: => Any): Unit = println(a)
def log(a: => Any): Unit
scala> def apply[A, B](a: A)(f: A => B) = f(a)
def apply[A, B](a: A)(f: A => B): B
scala> apply("hello")(log)
^
error: type mismatch;
found : (=> Any) => Unit
required: String => ?
scala> apply("hello")(log(_))
hello
scala> def log(a: Any) = println(a)
def log(a: Any): Unit
scala> def apply[A, B](a: A)(f: (=> A) => B) = f(a)
def apply[A, B](a: A)(f: (=> A) => B): B
scala> apply("hello")(log)
$line8.$read$$iw$$Lambda$1126/0x00000008010c5578@70022d44
scala> apply("hello")(identity)
^
error: type mismatch;
found : String => String
required: (=> String) => ?