This page is no longer maintained — Please continue to the home page at www.scala-lang.org

variance question

3 replies
Avi Pfeffer
Joined: 2009-02-23,
User offline. Last seen 42 years 45 weeks ago.

I have an abstract class Model with type parameter T that I want to
make covariant. It has a method called doEstimate that takes a T as
argument (so T is in contravariant position). However, doEstimate will
only be called from estimate, which takes anything as an argument and
will only call doEstimate if its argument is a T. This should be safe,
but I'm having trouble telling the compiler. I can't make doEstimate
private[this] because it is abstract:

> scala
Welcome to Scala version 2.7.3.final (Java HotSpot(TM) Client VM, Java
1.6.0_05).
Type in expressions to have them evaluated.
Type :help for more information.

scala> abstract class Model[+T] {
| def sample(): T
| def estimate(x: Any): Double = {
| x match {
| case t: T => doEstimate(t)
| case _ => 0.0
| }
| }
| private[this] def doEstimate(t: T): Double
| }
:12: error: abstract member may not have private modifier
private[this] def doEstimate(t: T): Double

I can get the effect I want by making estimate abstract, and
implementing the pattern matching in every subclass, but that results
in code duplication. Is there a better way?

Avi

James Iry
Joined: 2008-08-19,
User offline. Last seen 1 year 23 weeks ago.
Re: variance question
Avi,

Due to the way type erasure works, your pattern match won't ever work correctly even if the typing issue is solved

scala> class Model[T] {                 
     | def estimate(x : Any) : Double = {
// a T should result in 1.0 anything else should result in 0.0
     | x match {                        
     | case t : T => 1.0
     | case _ => 0.0
     | }
     | }
     | }

// note this warning!
warning: there were unchecked warnings; re-run with -unchecked for details
defined class Model

scala> new Model[String]
res0: Model[String] = Model@e38fca

// this looks fine
scala> res0.estimate("hello")
res1: Double = 1.0

// oops!
scala> res0.estimate(42)    
res2: Double = 1.0

reruning with scala -unchecked shows the problem
<console>:6: warning: abstract type T in type pattern is unchecked since it is eliminated by erasure
       case t : T => 1.0


On Wed, Feb 25, 2009 at 8:59 AM, Avi Pfeffer <avi@eecs.harvard.edu> wrote:
I have an abstract class Model with type parameter T that I want to
make covariant. It has a method called doEstimate that takes a T as
argument (so T is in contravariant position). However, doEstimate will
only be called from estimate, which takes anything as an argument and
will only call doEstimate if its argument is a T. This should be safe,
but I'm having trouble telling the compiler. I can't make doEstimate
private[this] because it is abstract:

> scala
Welcome to Scala version 2.7.3.final (Java HotSpot(TM) Client VM, Java
1.6.0_05).
Type in expressions to have them evaluated.
Type :help for more information.

scala> abstract class Model[+T] {
    |   def sample(): T
    |   def estimate(x: Any): Double = {
    |     x match {
    |       case t: T => doEstimate(t)
    |       case _ => 0.0
    |     }
    |   }
    |   private[this] def doEstimate(t: T): Double
    | }
<console>:12: error: abstract member may not have private modifier
        private[this] def doEstimate(t: T): Double

I can get the effect I want by making estimate abstract, and
implementing the pattern matching in every subclass, but that results
in code duplication. Is there a better way?

Avi

Robert Fischer
Joined: 2009-01-31,
User offline. Last seen 42 years 45 weeks ago.
Re: variance question

I've got an exploration of type erasure over on my blog, so you can get an idea about what's going
on: http://enfranchisedmind.com/blog/2007/05/17/the-generics-controversy/

It's an extremely similar situation I encountered a while back.

~~ Robert.

James Iry wrote:
> Avi,
>
> Due to the way type erasure works, your pattern match won't ever work
> correctly even if the typing issue is solved
>
> scala> class Model[T] {
> | def estimate(x : Any) : Double = {
> // a T should result in 1.0 anything else should result in 0.0
> | x match {
> | case t : T => 1.0
> | case _ => 0.0
> | }
> | }
> | }
>
> // note this warning!
> warning: there were unchecked warnings; re-run with -unchecked for details
> defined class Model
>
> scala> new Model[String]
> res0: Model[String] = Model@e38fca
>
> // this looks fine
> scala> res0.estimate("hello")
> res1: Double = 1.0
>
> // oops!
> scala> res0.estimate(42)
> res2: Double = 1.0
>
> reruning with scala -unchecked shows the problem
> :6: warning: abstract type T in type pattern is unchecked since
> it is eliminated by erasure
> case t : T => 1.0
>
>
> On Wed, Feb 25, 2009 at 8:59 AM, Avi Pfeffer > wrote:
>
> I have an abstract class Model with type parameter T that I want to
> make covariant. It has a method called doEstimate that takes a T as
> argument (so T is in contravariant position). However, doEstimate will
> only be called from estimate, which takes anything as an argument and
> will only call doEstimate if its argument is a T. This should be safe,
> but I'm having trouble telling the compiler. I can't make doEstimate
> private[this] because it is abstract:
>
> > scala
> Welcome to Scala version 2.7.3.final (Java HotSpot(TM) Client VM, Java
> 1.6.0_05).
> Type in expressions to have them evaluated.
> Type :help for more information.
>
> scala> abstract class Model[+T] {
> | def sample(): T
> | def estimate(x: Any): Double = {
> | x match {
> | case t: T => doEstimate(t)
> | case _ => 0.0
> | }
> | }
> | private[this] def doEstimate(t: T): Double
> | }
> :12: error: abstract member may not have private modifier
> private[this] def doEstimate(t: T): Double
>
> I can get the effect I want by making estimate abstract, and
> implementing the pattern matching in every subclass, but that results
> in code duplication. Is there a better way?
>
> Avi
>
>

Avi Pfeffer 2
Joined: 2009-02-25,
User offline. Last seen 42 years 45 weeks ago.
Re: variance question

James and Robert,

Thank you for explaining this. I hadn't realized erasure was at work here.

I think the correct solution to my problem is to only perform the
pattern match in subclasses where I know what T is.

For example,

class Test extends Model[Boolean] {
def estimate(x: Any) =
x match {
case b: Boolean => ...
case _ => 0.0
}
}

This way, I can let estimate take Any in Model and have T be
covariant. I do get a bit of code duplication but it's not too bad.

Avi

On Wed, Feb 25, 2009 at 12:57 PM, Robert Fischer
wrote:
> I've got an exploration of type erasure over on my blog, so you can get an
> idea about what's going on:
> http://enfranchisedmind.com/blog/2007/05/17/the-generics-controversy/
>
> It's an extremely similar situation I encountered a while back.
>
> ~~ Robert.
>
> James Iry wrote:
>>
>> Avi,
>>
>> Due to the way type erasure works, your pattern match won't ever work
>> correctly even if the typing issue is solved
>>
>> scala> class Model[T] {                     | def estimate(x : Any) :
>> Double = {
>> // a T should result in 1.0 anything else should result in 0.0
>>     | x match {                             | case t : T => 1.0
>>     | case _ => 0.0
>>     | }
>>     | }
>>     | }
>>
>> // note this warning!
>> warning: there were unchecked warnings; re-run with -unchecked for details
>> defined class Model
>>
>> scala> new Model[String]
>> res0: Model[String] = Model@e38fca
>>
>> // this looks fine
>> scala> res0.estimate("hello")
>> res1: Double = 1.0
>>
>> // oops!
>> scala> res0.estimate(42)    res2: Double = 1.0
>>
>> reruning with scala -unchecked shows the problem
>> :6: warning: abstract type T in type pattern is unchecked since
>> it is eliminated by erasure
>>       case t : T => 1.0
>>
>>
>> On Wed, Feb 25, 2009 at 8:59 AM, Avi Pfeffer > > wrote:
>>
>>    I have an abstract class Model with type parameter T that I want to
>>    make covariant. It has a method called doEstimate that takes a T as
>>    argument (so T is in contravariant position). However, doEstimate will
>>    only be called from estimate, which takes anything as an argument and
>>    will only call doEstimate if its argument is a T. This should be safe,
>>    but I'm having trouble telling the compiler. I can't make doEstimate
>>    private[this] because it is abstract:
>>
>>     > scala
>>    Welcome to Scala version 2.7.3.final (Java HotSpot(TM) Client VM, Java
>>    1.6.0_05).
>>    Type in expressions to have them evaluated.
>>    Type :help for more information.
>>
>>    scala> abstract class Model[+T] {
>>        |   def sample(): T
>>        |   def estimate(x: Any): Double = {
>>        |     x match {
>>        |       case t: T => doEstimate(t)
>>        |       case _ => 0.0
>>        |     }
>>        |   }
>>        |   private[this] def doEstimate(t: T): Double
>>        | }
>>    :12: error: abstract member may not have private modifier
>>            private[this] def doEstimate(t: T): Double
>>
>>    I can get the effect I want by making estimate abstract, and
>>    implementing the pattern matching in every subclass, but that results
>>    in code duplication. Is there a better way?
>>
>>    Avi
>>
>>
>
> --
> ~~ Robert Fischer.
> Grails Training        http://GroovyMag.com/training
> Smokejumper Consulting http://SmokejumperIT.com
> Enfranchised Mind Blog http://EnfranchisedMind.com/blog
>
> Check out my book, "Grails Persistence with GORM and GSQL"!
> http://www.smokejumperit.com/redirect.html
>

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland