Skip to content

Parameterless function identified as function with one Unit parameter #10437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
gaeljw opened this issue Nov 21, 2020 · 3 comments · Fixed by #14867
Closed

Parameterless function identified as function with one Unit parameter #10437

gaeljw opened this issue Nov 21, 2020 · 3 comments · Fixed by #14867

Comments

@gaeljw
Copy link

gaeljw commented Nov 21, 2020

Minimized code

import scala.language.implicitConversions

final class MyClass(name: String) {

  final class Fun0(val f: Function0[Any])

  object Fun0 {

    implicit def function0AsFun0(f: Function0[Any]): Fun0 = new Fun0(f)

  }

  def apply(f: => Unit): Unit = {
    apply(() => f)
  }

  def apply(fun: Fun0): Unit = {
    // Do something
    println(s"Got a Fun0 $fun")
  }

  def apply[T1](f: (T1) => Any)(implicit m1: Manifest[T1]): Unit = {
    // Do something
    println(s"Got a Function1: ${f}")
  }

}

Output

12 |    apply(() => f)
   |                  ^
   |                  No Manifest available for Unit.

From what I understand the () => f is interpreted as Unit => Any rather than () => Any.

Expectation

This code compiles fine with Scala 2.13. Although I admit it's a bit twisted, I expected it to compile in Scala 3 as well.

Note that I found two workarounds:

// 1st workaround
def apply(f: => Unit): Unit = {
  apply((() => f)) // Notice the double parenthesis
}

// 2nd workaround
def apply(f: => Unit): Unit = {
  val fun = () => f
  apply(fun) 
}

This might be intended behavior and I can accept it easily but I prefer to post and ask in case this could be the indicator of a more serious issue.

@bishabosha
Copy link
Member

bishabosha commented Nov 21, 2020

This is different behaviour to Scala 2. In Scala 2 the implicit conversion to Fun0 is activated with apply(() => f)

@som-snytt
Copy link
Contributor

The first two overloads are applicable, but the third is chosen in Scala 3.

There is a different error reported if renamed to avoid the overload:

➜  snips ~/scala3-3.0.0-M1/bin/scalac -d /tmp i10437d.scala
-- [E007] Type Mismatch Error: i10437d.scala:12:17 -----------------------------
12 |    apply3(() => f)
   |                 ^
   |                 Found:    () => Any
   |                 Required: Any => Any
-- Error: i10437d.scala:12:19 --------------------------------------------------
12 |    apply3(() => f)
   |                   ^
   |                   No Manifest available for Any.
2 errors found
➜  snips ~/scala3-3.0.0-M1/bin/scalac -d /tmp i10437.scala
-- Error: i10437.scala:12:18 ---------------------------------------------------
12 |    apply(() => f)
   |                  ^
   |                  No Manifest available for Unit.
1 error found

The first workaround makes it look like a syntactic parsing error, but the second workaround looks like a typing error during overload resolution.

@gaeljw
Copy link
Author

gaeljw commented Mar 7, 2021

For anyone coming here, with Scala 3 RC1 there is no more need for the implicit conversion to Fun0 thanks to the @targetName annotation and the following piece of code works as expected.

final class MyClass(name: String) {

  def apply(fun: => Any): Unit = {
    println("Got a By-name parameter")
  }

  @targetName("apply_fun0")
  def apply(fun: () => Any): Unit = {
    println("Got a Function0")
  }

  def apply[T1](f: T1 => Any): Unit = {
    println("Got a Function1")
  }

}

For instance, following code

MyClass("abc") {
  1
}

MyClass("abc") { () =>
  1
}

MyClass("abc") { (s: String) =>
  1
}

produces following output:

Got a By-name parameter
Got a Function0
Got a Function1

odersky added a commit to dotty-staging/dotty that referenced this issue Apr 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants