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

SAM treatment in typechecker doesn't differentiate between "literal function type" and function types #158

Closed
retronym opened this Issue Jun 18, 2016 · 5 comments

Comments

Projects
None yet
3 participants
@retronym
Member

retronym commented Jun 18, 2016

I would not expect an ambiguity in this case.

scala> object O {
     |   def foo(f: String => String) = 1
     |   def foo(f: java.util.function.Function[String, String]) = 2
     | }
defined object O

scala> def f[A]: A => A = null
f: [A]=> A => A

scala> O.foo(f)
<console>:14: error: overloaded method value foo with alternatives:
  (f: java.util.function.Function[String,String])Int <and>
  (f: String => String)Int
 cannot be applied to (Nothing => Nothing)
       O.foo(f)
         ^

given that the does not typecheck:

scala> object O {
     |   def foo(f: java.util.function.Function[String, String]) = 2
     | }
defined object O

scala> def f[A]: A => A = null
f: [A]=> A => A

scala> O.foo(f)
<console>:14: error: type mismatch;
 found   : String => String
 required: java.util.function.Function[String,String]
       O.foo(f)
             ^

@retronym retronym added the t:sam label Jun 18, 2016

@retronym retronym added this to the 2.12.0-RC1 milestone Jun 18, 2016

@retronym

This comment has been minimized.

Show comment
Hide comment
@retronym

retronym Jun 18, 2016

Member

isCompatible(FunctionN, SamType) is true.

Member

retronym commented Jun 18, 2016

isCompatible(FunctionN, SamType) is true.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Jul 28, 2016

Member

The conclusion here is that the status quo makes sense by interpreting it as an implicit conversion that imposes a syntactic constraint (the argument has to be a function literal). Kind of how PartialFunction synthesis requires a pattern match and implicits need not succeed even if isCoercible says true (bounds could fail).

Member

adriaanm commented Jul 28, 2016

The conclusion here is that the status quo makes sense by interpreting it as an implicit conversion that imposes a syntactic constraint (the argument has to be a function literal). Kind of how PartialFunction synthesis requires a pattern match and implicits need not succeed even if isCoercible says true (bounds could fail).

@adriaanm adriaanm closed this Jul 28, 2016

@lrytz lrytz added the release-notes label Oct 16, 2016

@lrytz

This comment has been minimized.

Show comment
Hide comment
@lrytz

lrytz Oct 16, 2016

Member

A related example:

scala> object T {
     |   def m(f: () => Unit) = 0
     |   def m(r: Runnable) = 1
     | }
defined object T

scala> T.m(() => ())
res0: Int = 0

scala> T.m(null)
res1: Int = 0

In 2.11.x, the second call T.m(null) is ambiguous. I guess the goal of isCompatibleSam is to make the first call T.m(() => ()) work without ambiguity - which is arguably arbitrary, but probably necessary for compatibility.

Member

lrytz commented Oct 16, 2016

A related example:

scala> object T {
     |   def m(f: () => Unit) = 0
     |   def m(r: Runnable) = 1
     | }
defined object T

scala> T.m(() => ())
res0: Int = 0

scala> T.m(null)
res1: Int = 0

In 2.11.x, the second call T.m(null) is ambiguous. I guess the goal of isCompatibleSam is to make the first call T.m(() => ()) work without ambiguity - which is arguably arbitrary, but probably necessary for compatibility.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Oct 17, 2016

Member

This is covered by this example from the spec, but it could probably be improved to make the syntactic assumption explicit. Attempt in bold.

Function compatibility via SAM conversion

Given the definitions

def foo(x: Int => String): Unit
def foo(x: ToString): Unit

trait ToString { def convert(x: Int): String }

The application foo((x: Int) => x.toString) resolves to the first overload,
as it's more specific:

  • Int => String is compatible to ToString -- when expecting a value of type ToString, you may pass a function literal from Int to String, as it will be SAM-converted to said function (this is true for some values of type Int => String, not all of them, as SAM-conversion only applies to function literals);
  • ToString is not compatible to Int => String -- when expecting a function from Int to String, you may not pass a ToString.
Member

adriaanm commented Oct 17, 2016

This is covered by this example from the spec, but it could probably be improved to make the syntactic assumption explicit. Attempt in bold.

Function compatibility via SAM conversion

Given the definitions

def foo(x: Int => String): Unit
def foo(x: ToString): Unit

trait ToString { def convert(x: Int): String }

The application foo((x: Int) => x.toString) resolves to the first overload,
as it's more specific:

  • Int => String is compatible to ToString -- when expecting a value of type ToString, you may pass a function literal from Int to String, as it will be SAM-converted to said function (this is true for some values of type Int => String, not all of them, as SAM-conversion only applies to function literals);
  • ToString is not compatible to Int => String -- when expecting a function from Int to String, you may not pass a ToString.
@lrytz

This comment has been minimized.

Show comment
Hide comment
@lrytz

lrytz Oct 18, 2016

Member

It does, thanks.

Member

lrytz commented Oct 18, 2016

It does, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment