Skip to content
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

Ambiguous method overloading compiles, but should not #2628

Closed
scabug opened this issue Nov 13, 2009 · 13 comments
Closed

Ambiguous method overloading compiles, but should not #2628

scabug opened this issue Nov 13, 2009 · 13 comments
Assignees

Comments

@scabug
Copy link

scabug commented Nov 13, 2009

The following compiles, yet neither method is callable due to ambiguity (although I have yet to be pointed to where in the spec that is considered ambiguous).

object Foo {
  def bar(i: Int) = println(i)
  def bar(i: Int) (f: Float) = println(i*f)
}
@scabug
Copy link
Author

scabug commented Nov 13, 2009

Imported From: https://issues.scala-lang.org/browse/SI-2628?orig=1
Reporter: @nilskp

@scabug
Copy link
Author

scabug commented Nov 16, 2009

@lrytz said:
the spec of overloading resolution handles curried applications one by one. for the application

  foo.bar(10)(2.0f)

overloading resolution is done on the application foo.bar(10) without looking at the second parameter list.

@scabug
Copy link
Author

scabug commented Nov 16, 2009

@nilskp said:
Replying to [comment:2 rytz]:

the spec of overloading resolution handles curried applications one by one.

Could you point me to the exact place in the spec?

overloading resolution is done on the application foo.bar(10) without looking at the second parameter list.

Is there a particular reason for this? (I assume there is, so I'm fishing for the reason).

@scabug
Copy link
Author

scabug commented Nov 16, 2009

@lrytz said:
Replying to [comment:3 nilskp]:

Could you point me to the exact place in the spec?

Nightly build: http://www.scala-lang.org/node/212/pdfs, Section 6.25.3

overloading resolution is done on the application foo.bar(10) without looking at the second parameter list.

Is there a particular reason for this? (I assume there is, so I'm fishing for the reason).

Simplicity I guess (in both, spec and implementation)

@scabug
Copy link
Author

scabug commented Nov 17, 2009

@odersky said:
In fact you can distinguish between the two alternatives by giving an expected result type.

@scabug
Copy link
Author

scabug commented Nov 17, 2009

@nilskp said:
In both cases the expected result type is Unit. Could you please give an example of how these two methods can be called? I have tried the following, all of which fails (presumably because the return type is the same for both methods?)

  bar(5): Unit
  val u1: Unit = bar(5)
  bar(5)(2f): Unit
  val u2: Unit = bar(5)(2f)

@scabug
Copy link
Author

scabug commented Nov 17, 2009

@adriaanm said:
no, the result type is not the same if you partially apply the methods: bar(5): Unit vs bar(5): Float => Unit

@scabug
Copy link
Author

scabug commented Nov 17, 2009

@nilskp said:
Replying to [comment:7 moors]:

no, the result type is not the same if you partially apply the methods

But if I don't partially apply, they are.

bar(5): Unit vs bar(5): Float => Unit

Both fail. Please try before closing again.

These are the two errors I get:

  bar(5): Unit // ambiguous reference to overloaded definition, both method bar in object Foo of type (i: Int)(f: Float)Unit and  method bar in object Foo of type (i: Int)Unit match argument types (Int)
  bar(5): Float => Unit // not a legal formal parameter
  bar(5): (Float => Unit) // ambiguous reference to overloaded definition, both method bar in object Foo of type (i: Int)(f: Float)Unit and  method bar in object Foo of type (i: Int)Unit match argument types (Int)

@scabug
Copy link
Author

scabug commented Nov 17, 2009

@adriaanm said:
Sorry, thanks for persisting (in the future, it would help, though, to simply copy/paste output from the interpreter, as I've done below)

Martin: I tried out various combinations of argument types, and could refer to the overload with one argument list (except not in the given example -- see below for one where I did manage), but never to the one with two.

scala> object Foo {
     |   def bar(i: Int) = println(i)
     |   def bar(i: Int) (f: Float) = println(i*f)
     | }
defined module Foo

scala> Foo.bar(5): (Float => Unit)
<console>:6: error: ambiguous reference to overloaded definition,
both method bar in object Foo of type (i: Int)(f: Float)Unit
and  method bar in object Foo of type (i: Int)Unit
match argument types (Int)
       Foo.bar(5): (Float => Unit)
           ^

scala> object x{def a(i: String): Option[String] = Some("a"); def a(i: String)(b:  List[String]): Int = 1}
defined module x

scala> x.a("a"): Option[String]
res10: Option[String] = Some(a)

scala> def f(xs: List[String]): Int = x.a("a")(xs)                         
<console>:5: error: ambiguous reference to overloaded definition,
both method a in object x of type (i: String)(b: List[String])Int
and  method a in object x of type (i: String)Option[String]
match argument types (java.lang.String)
       def f(xs: List[String]): Int = x.a("a")(xs)
                                        ^

@scabug
Copy link
Author

scabug commented Nov 17, 2009

@odersky said:
I see, but would classify this as a corner case due to the unit conversion. There's no attempt in the spec to classify overloads that will always yield ambiguous errors when referenced as illegal. I believe adding such rules would be quite tricky, and it's not a priority for us.

@scabug
Copy link
Author

scabug commented Nov 17, 2009

@nilskp said:
Replying to [comment:10 odersky]:

I see, but would classify this as a corner case due to the unit conversion. There's no attempt in the spec to classify overloads that will always yield ambiguous errors when referenced as illegal.

That would be fine if there was some way to call the second method. There is not (that I know of, short of using reflection).
I was using Eclipse, which is why I couldn't easily copy/paste my results. Apologize for that. Here's more using a slightly modified version (returning Float) from the console:

scala> object Foo {def bar(i:Int):Float=i;def bar(i:Int)(f:Float)=i*f;}
defined module Foo

scala> Foo.bar(5)
<console>:6: error: ambiguous reference to overloaded definition,
both method bar in object Foo of type (Int)(Float)Float
and  method bar in object Foo of type (Int)Float
match argument types (Int)
       Foo.bar(5)
           ^

scala> Foo.bar(5)(3.2f)
<console>:6: error: ambiguous reference to overloaded definition,
both method bar in object Foo of type (Int)(Float)Float
and  method bar in object Foo of type (Int)Float
match argument types (Int)
       Foo.bar(5)(3.2f)
           ^

scala> Foo.bar(5): Float
res2: Float = 5.0

scala> Foo.bar(5)(3.2f): Float
<console>:6: error: ambiguous reference to overloaded definition,
both method bar in object Foo of type (Int)(Float)Float
and  method bar in object Foo of type (Int)Float
match argument types (Int)
       Foo.bar(5)(3.2f): Float
           ^

scala> Foo.bar(5) _
<console>:6: error: ambiguous reference to overloaded definition,
both method bar in object Foo of type (Int)(Float)Float
and  method bar in object Foo of type (Int)Float
match argument types (Int)
       Foo.bar(5) _
           ^

scala> Foo.bar(5): (Float=>Float)
<console>:6: error: ambiguous reference to overloaded definition,
both method bar in object Foo of type (Int)(Float)Float
and  method bar in object Foo of type (Int)Float
match argument types (Int)
       Foo.bar(5): (Float=>Float)
           ^

It does not seem right that one can define methods that simply cannot be called. I would much prefer that both methods could be called, since they do not appear ambiguous to me

  Foo.bar(5)      // Unambiguously calls the single parm variant
  Foo.bar(5) _    // Unambiguously calls the double parm variant partially
  Foo.bar(5)(2f)  // Unambiguously calls the double parm variant.

Short of that, it seems that the same ambiguity rules should be applied to defs, preventing creation of methods that are impossible to reference.

@scabug
Copy link
Author

scabug commented Nov 17, 2009

@odersky said:
"Short of that, it seems that the same ambiguity rules should be applied to defs, preventing creation of methods that are impossible to reference." I just wrote above that there is no attempt to do such a thing in the spec. I challenge you to give a set of complete and decidable rules to do this.

@scabug
Copy link
Author

scabug commented Nov 18, 2009

@nilskp said:
So in Scala, it should be possible to define methods that no one can call? That's a rather odd goal.
As I've said before, I would prefer that the methods could actually be called, rather than ambiguity being determined by the first argument list only, but that is also not a goal.
So that leaves one with a rather odd situation of having methods that compile fine, and can be unambiguously referenced by any human, yet the Scala compiler, which has a general goal in other aspects to be smart, cannot.

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

No branches or pull requests

2 participants