- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
variance question
Wed, 2009-02-25, 18:00
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
Wed, 2009-02-25, 19:07
#2
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
>
>
Wed, 2009-02-25, 20:57
#3
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
>
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: