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

Failing to understand Parametric Types Variances

4 replies
mailleux
Joined: 2008-08-23,
User offline. Last seen 4 years 7 weeks ago.
I have been trying to  get this code to work, and have managed using abstract classes but I believe co-variances and contra-variances should do the trick. I've read several examples, but fail to make my code work.

I have a class that holds values:

class Holder[T](init:T) {
  var _v:T=init

  def value:T=_v
  def value_=(nv:T) { _v=nv }
}

And I want to put Holder[T] of various types T in it. This code is obviously not going to do the trick. But it compiles:

class Container[T] {

   var lst:List[Holder[T]]=Nil

   def add(h:Holder[T]) { lst=h :: lst }
}

The idea is to be able to do :

val cont=new Container....

cont.add(new Holder[Int](10))
cont.add(new Holder[String]("test"))

I need to have them as Holder (so I can't cast them to Any). But I fail to make Container work:

Container[+T] does not compile with issues on lst
Container[-T] does work either.

I'm sure I'm missing the way to do this. Could any one explain me what I'm missing?

Thomas


ewilligers
Joined: 2008-08-20,
User offline. Last seen 3 years 17 weeks ago.
Re: Failing to understand Parametric Types Variances

Thomas Sant Ana wrote:
> I have a class that holds values

> I want to put Holder[T] of various types T in it.

class Container {
var lst:List[Holder[_]] = Nil

def add(h:Holder[_]) { lst = h :: lst }
}

or equivalently

class Container {
var lst:List[Holder[T] forSome { type T } ] = Nil

def add(h:Holder[T] forSome { type T } ) { lst = h :: lst }
}

val cont=new Container

cont.add(new Holder[Int](10))
cont.add(new Holder[String]("test"))

Richard
Joined: 2008-12-26,
User offline. Last seen 42 years 45 weeks ago.
Re: Failing to understand Parametric Types Variances

>
> class Container {
> var lst:List[Holder[_]] = Nil
>
> def add(h:Holder[_]) { lst = h :: lst }
> }
>

Using existential types seems a bit like "cheating".. Isn't it possible to solve
this using bounded types so that Container[+T] is valid? (I'm currently trying
to do this but getting stuck ... and tired)

Richard

Carsten Saager
Joined: 2008-12-19,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Failing to understand Parametric Types Variances
I think there is a misunderstanding what covariance means

A Container[+T] will satisfy Container[Double]<:Container[Any], it looks like you want to have a type bound to ensure that the elements in Container are assignablt to T; for this Container[T] is sufficient

class Container[T] {
  var lst:List[Holder[T]]=Nil

  def add(h:Holder[T]) { lst=h :: lst }
}

val container = new Container[AnyVal]
  
container.add( new Holder(1.2)) //ok
container.add( new Holder(1)) // ok
container.add( new Holder("string")) // error

Holder doesn't need to be covariant here as the compiler infers here to create a Holder[AnyVal] and any Double or Int is an AnyVal, thus the constructor call succeeds. If you try to be to explicit by writing Holder[Int](1) it'll fail.


/Carsten  

On Fri, Dec 26, 2008 at 5:31 PM, Richard <optevo [at] gmail [dot] com> wrote:

>
> class Container {
>      var lst:List[Holder[_]] = Nil
>
>      def add(h:Holder[_]) { lst = h :: lst }
> }
>


Using existential types seems a bit like "cheating".. Isn't it possible to solve
this using bounded types so that Container[+T] is valid? (I'm currently trying
to do this but getting stuck ... and tired)

Richard




ewilligers
Joined: 2008-08-20,
User offline. Last seen 3 years 17 weeks ago.
Re: Failing to understand Parametric Types Variances

Richard wrote:
>> class Container {
>> var lst:List[Holder[_]] = Nil
>>
>> def add(h:Holder[_]) { lst = h :: lst }
>> }
>>
>
>
> Using existential types seems a bit like "cheating".. Isn't it possible to solve
> this using bounded types so that Container[+T] is valid? (I'm currently trying
> to do this but getting stuck ... and tired)

Not possible. If it was, I wouldn't need the asInstanceOf cast in the
unsafe code below.

abstract class AbstractHolder[+T] {
def value: T
}

class Holder[T](init: T) extends AbstractHolder[T] {
private var _v: T = init

def value: T = _v
def value_=(nv: T) { _v = nv }
}

abstract class AbstractContainer[+T] {
def lst: List[AbstractHolder[T]]
}

class Container[T] extends AbstractContainer[T] {
private var _lst: List[Holder[T]] = Nil

def lst = _lst
def add(h: Holder[T]) { _lst = h :: _lst }
}

object Test extends Application {
val ha = new Holder[Any]("")
val cs = new Container[String]
val ca: AbstractContainer[Any] = cs
(ca.asInstanceOf[Container[Any]]).add(ha)
var s: String = cs.lst.head.value
ha.value=this // or anything else other than a String
s = cs.lst.head.value // ClassCastException
}

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