Skip to content

Overloading doesn't work with contextual functions #7790

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
anatoliykmetyuk opened this issue Dec 17, 2019 · 9 comments · Fixed by #17473
Closed

Overloading doesn't work with contextual functions #7790

anatoliykmetyuk opened this issue Dec 17, 2019 · 9 comments · Fixed by #17473

Comments

@anatoliykmetyuk
Copy link
Contributor

anatoliykmetyuk commented Dec 17, 2019

minimized code

trait Foo
  given Int = 10
  def map(f: Int ?=> Int) = f
  def map(f: Int ?=> String) = f

@main def Test =
  val m: Foo = ???
  m.map((using x: Int) => x)

Says:

-- [E134] Type Mismatch Error: /Users/kmetiuk/Projects/scala3/pg/sandbox/iss1.scala:8:4
8 |  m.map((given x: Int) => x)
  |  ^^^^^
   |None of the overloaded alternatives of method map in trait Foo with types
   | (f: (Int) ?=> String): String
   | (f: (Int) ?=> Int): Int
   |match arguments ((Int) ?=> Int)
1 error found

expectation

Clearly the alternative (f: (Int) ?=> Int): Int should match the argument (Int) ?=> Int.

@bishabosha
Copy link
Member

bishabosha commented Dec 17, 2019

This is actually masking a different error, i.e. you can't define two overloads with a single Function1 argument as they are the same after erasure

@anatoliykmetyuk
Copy link
Contributor Author

It works without givens. Some functions work while others don't, see #7792.

@bishabosha
Copy link
Member

Ah ok, different situations produce different results, thanks for pointing out

trait Foo
  given Int = 10
  def map(f: (Int) => Int) = f
  def map(f: (Int) => String) = f

@main def Test =
  val m: Foo = ???
  m.map((given x: Int) => x)

^ by purely removing given in the case above with 0.20.0-RC1 i get

-- [E120] Duplicate Symbol Error: test.scala:4:6 -------------------------------
4 |  def map(f: (Int) => String) = f
  |      ^
  |      Double definition:
  |      def map(f: Int => Int): Int => Int in trait Foo at line 3 and
  |      def map(f: Int => String): Int => String in trait Foo at line 4
  |      have the same type after erasure.
-- Error: test.scala:8:2 -------------------------------------------------------
8 |  m.map((given x: Int) => x)
  |  ^
  |  cannot merge
  |    method map in trait Foo of type (f: Int => String): Int => String  and
  |    method map in trait Foo of type (f: Int => Int): Int => Int
  |  they are both defined in trait Foo but have matching signatures
  |    (f: Int => String): Int => String and
  |    (f: Int => Int): Int => Int
  |  as members of value m
  |         
two errors found

@anatoliykmetyuk
Copy link
Contributor Author

anatoliykmetyuk commented Dec 17, 2019

If you actually call f at the right hand side of your maps on something, the error should go away. But yeah, interactions of lambdas and overloading look flaky.

@bishabosha
Copy link
Member

Ah interesting, At the JVM level the return type distinguishes two overloads with the same parameters, but the Java language only distinguishes overloads by the arguments apparently.

@bishabosha
Copy link
Member

This example is not accepted by java, even though the method signatures are essentially what trait Foo produces

public interface Bar {
    default public int map(java.util.function.Function<Object, Object> f) {
        return 0;
    }

    default public String map(java.util.function.Function<Object, String> f) {
        return "";
    }
}

@Adam-Vandervorst
Copy link

Is there a workaround for this? Not being able to overload higher-order functions is something I run into often.

It sometimes takes a while to figure out why they would be equal too, is there a way to see the signatures after erasure?

[error]    |Double definition:
[error]    |def ul: ((org.scalajs.dom.html.UList) ?=> Unit) => (org.scalajs.dom.html.Element
[error]    |  )
[error]    | ?=> org.scalajs.dom.html.UList in package object lib$package at line 60 and
[error]    |def ul: (String, String, String, String) => ((org.scalajs.dom.html.UList) ?=> 
[error]    |  Unit
[error]    |) => (org.scalajs.dom.html.Element) ?=> org.scalajs.dom.html.UList in package object lib$package at line 61
[error]    |have the same type after erasure.

@nicolasstucki
Copy link
Contributor

Updated syntax

trait Foo:
  given Int = 10
  def map(f: Int ?=> Int) = f
  def map(f: Int ?=> String) = f

@main def Test =
  val m: Foo = ???
  m.map((x: Int) ?=> x)
8 |  m.map((x: Int) ?=> x)
  |        ^^^^^^^^^^^^^^
  |        Found:    Int
  |        Required: Int => <?>

@odersky odersky self-assigned this Apr 5, 2022
@ckipp01
Copy link
Member

ckipp01 commented May 11, 2023

Revisiting this, this now seems to compile as expected:

//> using scala 3.3.1-RC1-bin-20230510-d6c643c-NIGHTLY

trait Foo:
  given Int = 10
  def map(f: Int ?=> Int) = f
  def map(f: Int ?=> String) = f

@main def Test =
  val m: Foo = ???
  m.map((x: Int) ?=> x)

I'll go ahead and close, but please do report back if I'm misunderstanding something and this shouldn't be closed.

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.

6 participants