bug icon indicating copy to clipboard operation
bug copied to clipboard

A and (=> A) do not unify when used as function type parameters

Open scabug opened this issue 12 years ago • 7 comments

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

scabug avatar Apr 28 '13 21:04 scabug

Imported From: https://issues.scala-lang.org/browse/SI-7431?orig=1 Reporter: Dan Rosen (mergeconflict) Blocks #7430

scabug avatar Apr 28 '13 21:04 scabug

@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.

scabug avatar Apr 28 '13 22:04 scabug

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...

scabug avatar Apr 29 '13 16:04 scabug

@paulp said: The key word is "converted".

scabug avatar Apr 29 '13 21:04 scabug

Maybe this hasn't been fixed yet because it both should and should not compile.

I hear Kentucky Mule can do both in parallel.

hrhino avatar Feb 22 '18 16:02 hrhino

The situation doesn't seem to have changed in Scala 3. Even the ClassCastException remains reproducible.

SethTisue avatar Aug 25 '22 17:08 SethTisue

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) => ?

som-snytt avatar Aug 25 '22 18:08 som-snytt