Strange type error

5 views
Skip to first unread message

Alexey Romanov

unread,
Mar 10, 2011, 2:15:00 AM3/10/11
to scala-user
Today I received a comment to my StackOverflow question from a year
ago: http://stackoverflow.com/questions/2873575/scala-2-8-weird-type-error
suggesting it could be a compiler bug. I thought I asked on the
mailing list back then as well, but couldn't find it in the archives.

I've minimized the example given there to:

object Main {
class NonGeneric {}
class Generic[T] {}

class Composite {
def contains(setup : Composite => Unit) : Composite = this
}

def generic[T](parent: Composite): Generic[T] = new Generic[T]
def nonGeneric(parent: Composite): NonGeneric = new NonGeneric

new Composite().contains(
nonGeneric // should have type Composite => NonGeneric
)

new Composite().contains(
generic[Int] // should have type Composite => Generic[Int]
)
}

When compiling (with Scala 2.8.1 final), I get this error:

Composite.scala:17: error: polymorphic expression cannot be
instantiated to expected type;
found : => (Main.Composite) => Main.Generic[Int]
required: (Main.Composite) => Unit
generic[Int] // should have type Composite => Generic[Int]
^
one error found

but no problem with nonGeneric.

1. Should there be a difference, or is this really a bug?
2. Why is the found type not "(Main.Composite) => Main.Generic[Int]"?

Yours, Alexey Romanov

Brian Maso

unread,
Mar 10, 2011, 11:20:15 AM3/10/11
to Alexey Romanov, scala-user
This is pretty wierd -- it seems that the non-parametric function nonGeneric can automatically (implicitly?) be converted from "Composite => NonGeneric" to "Composite => Unit":

scala> object Main {
     |   class NonGeneric {}
     |
     |   class Composite {}

     |
     |   def nonGeneric(parent: Composite): NonGeneric = new NonGeneric
     |   }
defined module Main

scala> val func = Main.nonGeneric _
func: (Main.Composite) => Main.NonGeneric = <function1>

scala> func.asInstanceOf[Main.Composite => Unit]
res7: (Main.Composite) => Unit = <function1>

scala> // Wow! That was surprising! What conversion is happening there?

And in fact the exact same conversion will happen with a function built from a parametrically-typed method as well:

scala> object Main {

     |   class Generic[T] {}
     |
     |   class Composite {}
     |
     |   def generic[T](parent: Composite): Generic[T] = new Generic[T]
     |   }
defined module Main

scala> val func = Main.generic[Int] _
func: (Main.Composite) => Main.Generic[Int] = <function1>

scala> func.asInstanceOf[Main.Composite => Unit]
res8: (Main.Composite) => Unit = <function1>

scala> // Ok... That seems to work fine with a parametric method...


However, below you can see the automatic conversion will not be applied by the compiler for the function built from a parametrically-typed method:

scala> object Main {

     |   class Generic[T] {}
     |
     |   class Composite {
     |     def contains(setup: Composite => Unit): Composite = this
     |     }
     |
     |   def generic[T](parent: Composite): Generic[T] = new Generic[T]
     |
     |   new Composite().contains(generic[Int] _)
     |
     |   }
<console>:14: error: polymorphic expression cannot be instantiated to expected type;

 found   : => (Main.Composite) => Main.Generic[Int]
 required: (Main.Composite) => Unit
         new Composite().contains(generic[Int] _)
                                         ^

You can go ahead and apply an explicit conversion to "Composite => Unit" to workaround the compiler's shortcoming:

scala> object Main {

     |   class Generic[T] {}
     |
     |   class Composite {
     |     def contains(setup: Composite => Unit): Composite = this
     |     }
     |
     |   def generic[T](parent: Composite): Generic[T] = new Generic[T]
     |
     |   new Composite().contains((generic[Int] _).asInstanceOf[Composite => Unit])
     |
     |   }
defined module Main

scala> // Why is the explicit conversion necessary?


Wierd that the explicit conversion is necessary.

Can anyone spread some light on how the automatic conversion to "Composite => Unit" is happening? What tools can I use to explore/illuminate the implicit conversions being applied during a REPL session?

Brian Maso
--
Best regards,
Brian Maso
(949) 395-8551
br...@blumenfeld-maso.com

Hubert Plociniczak

unread,
Mar 10, 2011, 12:33:33 PM3/10/11
to scala...@googlegroups.com
On 03/10/2011 08:15 AM, Alexey Romanov wrote:
> Today I received a comment to my StackOverflow question from a year
> ago: http://stackoverflow.com/questions/2873575/scala-2-8-weird-type-error
> suggesting it could be a compiler bug. I thought I asked on the
> mailing list back then as well, but couldn't find it in the archives.
>
> I've minimized the example given there to:
>
> object Main {
> class NonGeneric {}
> class Generic[T] {}
>
> class Composite {
> def contains(setup : Composite => Unit) : Composite = this
> }
>
> def generic[T](parent: Composite): Generic[T] = new Generic[T]
> def nonGeneric(parent: Composite): NonGeneric = new NonGeneric
>
> new Composite().contains(
> nonGeneric // should have type Composite => NonGeneric
> )
>
> new Composite().contains(
> generic[Int] // should have type Composite => Generic[Int]
> )
> }

What happens is that we perform eta-expansion on nonGeneric and
generic[Int].
In the first case we adapt the eta-expanded expression to the expected
type i.e. Unit so you get something along the lines of
nonGeneric becomes {(parent0: Composite) =>
Main.this.nonGeneric(parent0); ()}

In the second second case adaption is not done with expected type and
later we fail with the implicit search (because there aren't any).

I will have to check if there is a reason for that.
And no, implicits have nothing to do in this case.

Thanks,
hubert

Jason Zaugg

unread,
Mar 10, 2011, 12:57:06 PM3/10/11
to Hubert Plociniczak, scala...@googlegroups.com
On Thu, Mar 10, 2011 at 6:33 PM, Hubert Plociniczak
<hubert.pl...@epfl.ch> wrote:
> In the second second case adaption is not done with expected type and later
> we fail with the implicit search (because there aren't any).
>
> I will have to check if there is a reason for that.
> And no, implicits have nothing to do in this case.

I think this part [1] of the type checker, which references #2624, is relevent.

-jason

[1] http://bit.ly/gsYPT7
[2] lampsvn.epfl.ch/trac/scala/ticket/2624

Hubert Plociniczak

unread,
Mar 10, 2011, 1:28:14 PM3/10/11
to Jason Zaugg, scala...@googlegroups.com
On 03/10/2011 06:57 PM, Jason Zaugg wrote:
> On Thu, Mar 10, 2011 at 6:33 PM, Hubert Plociniczak
> <hubert.pl...@epfl.ch> wrote:
>> In the second second case adaption is not done with expected type and later
>> we fail with the implicit search (because there aren't any).
>>
>> I will have to check if there is a reason for that.
>> And no, implicits have nothing to do in this case.
> I think this part [1] of the type checker, which references #2624, is relevent.

Yeap, I was looking at the same thing.

hubert

Hubert Plociniczak

unread,
Mar 14, 2011, 10:09:01 AM3/14/11
to scala...@googlegroups.com
fyi created #4336 for the issue.

hubert

Reply all
Reply to author
Forward
0 new messages