markiz
markiz

Reputation: 265

Scala type system and inheritance

Take the following Scala code:

import scala.collection.mutable.HashMap

class father[T,K:Numeric] extends HashMap[T,K]
class sonA[T] extends father[T, Long]
class sonB[T] extends father[T, Double]

def func_sonA[T](x: List[T]) = {
    // code
    new sonA[T]
}

def func_sonB[T](x: List[T]) = {
    // code
    new sonB[T]
}

val strList = List("one", "two", "three", "four")
val intList = List(1,2,3,4)
val both:List[List[Any]] = List(strList, intList)

val mapped = both.map(x =>
    x match {
        case i:List[String] => func_sonA(i)
        case i:List[Int] => func_sonB(i)
    }
)

The type for mapped is: List[father[_ >: String with Int, _ >: Long with Double : AnyVal]].

What exactly does _>: means?

What's the idea behind String with Int and what is it good for? Seems that it can grow indefinitely (String with Int with Double With Long with...).

How can I specify the type of mapped to be just List[father] or List[father[Any, Any]] without losing the type information?

I'm having a bit of a difficulty finding answers since search results for phrases _>: aren't very relevant.


EDIT:

To illustrate the actual problem its causing, I want to use a function with a List[father[Any, Any]] input but I can't pass mapped. I'm getting a type mismatch error.

Function:

def bar(x: List[father[Any, Any]]) = {
    println(x)
}
bar(mapped)

Error:

Error:(52, 9) type mismatch;
 found   : List[father[_ >: String with Int, _ >: Long with Double <: AnyVal]]
 required: List[father[Any,Any]]
    bar(mapped)
        ^

Upvotes: 1

Views: 151

Answers (1)

thwiegan
thwiegan

Reputation: 2173

_ >: String with Int basically means, that the type is either String OR Int. In case of List[String] you get back a sonA[String] and therefore a father[String, Long], in case of List[Int] respectively a sonB[Int] and therefore a father[Int,Double] so you end up with a father where the first type is either String or Int and the second type is either Long or Double.

Regarding your other question:

How can I specify the type of mapped to be just List[father] or List[father[Any, Any]] without losing the type information?

Why do you want that? The current type is actually more specific than just List[father] or List[father[Any,Any]].

EDIT

Okay now I know what you want.

Basically what you want to achieve is that this becomes possible:

val mapped: List[father[Any,Any]] = both.map(x =>
  x match {
    case i:List[String] => func_sonA(i)
    case i:List[Int] => func_sonB(i)
  }
)

The problem here is, that currently with

class father[T,K] extends HashMap[T,K]

The scala compiler does not know that e.g. father[String,Long] is supposed to be a subtype of father[Any,Any] (this does not automatically result from String and Long being subtypes of Any). So, to tell Scala that the above should be applicable, you need the class father to be covariant in both type parameters:

class father[+T,+K] 

The problem with that is, that you are extending HashMap[T,K]. But this class is only covariant in the first type Parameter T, but not K.

So this code here actually works, but it is not extending HashMap:

class father[+T,+K]
class sonA[T] extends father[T, Long]
class sonB[T] extends father[T, Double]

def func_sonA[T](x: List[T]) = {
  // code
  new sonA[T]
}

def func_sonB[T](x: List[T]) = {
  // code
  new sonB[T]
}

val strList = List("one", "two", "three", "four")
val intList = List(1,2,3,4)
val both = List(strList, intList)

val mapped: List[father[Any,Any]] = both.map(x =>
  x match {
    case i:List[String] => func_sonA(i)
    case i:List[Int] => func_sonB(i)
  }
)

def bar(x: List[father[Any, Any]]) = {
  println(x)
}
bar(mapped)

Further readings on covariance here.

Upvotes: 1

Related Questions