Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can the ListNode example (Scala website) handle different types?

The ListNode example, taken from Scalas homepage goes like this:

case class ListNode[+T](h: T, t: ListNode[T]) {
  def head: T = h
  def tail: ListNode[T] = t
  def prepend[U >: T](elem: U): ListNode[U] =
    ListNode(elem, this)
}

With this class, we can create objects like:

val empty: ListNode[Null] = ListNode(null, null)
val strList: ListNode[String] = empty.prepend("hello")
                                     .prepend("world")
val anyList: ListNode[Any] = strList.prepend(12345)

As we can see, we can prepend an integer value to a String node. I guess, this works because the type parameter U will be automatically set to Any, when given the integer to the prepend method (because Int is not a supertype of String).

When trying this with an own lower bound example, I will get an error:

scala> class E[T >: String]
defined class E

scala> new E[Any]
res1: E[Any] = E@135f0a

scala> new E[Int]
<console>:11: error: type arguments [Int] do not conform to class E's type param
eter bounds [T >: String]
      val res2 =
          ^
<console>:12: error: type arguments [Int] do not conform to class E's type param
eter bounds [T >: String]
              new E[Int]
                  ^

Why is the type Int here not automatically seen as type Any like in the ListNode example ?


UPDATE 1: This also works (without explicitley saying the new ListNode should be of type Any)

scala> val empty: ListNode[Null] = ListNode(null, null)
empty: example.listNode.ListNode[Null] = ListNode(null,null)

scala> empty.prepend("hello").prepend("world")
res0: example.listNode.ListNode[java.lang.String] = ListNode(world,ListNode(hell
o,ListNode(null,null)))

scala> val strList: ListNode[String] = empty.prepend("hello").prepend("world")
strList: example.listNode.ListNode[String] = ListNode(world,ListNode(hello,ListN
ode(null,null)))

scala> strList.prepend(12345)
res1: example.listNode.ListNode[Any] = ListNode(12345,ListNode(world,ListNode(he
llo,ListNode(null,null))))
like image 781
John Threepwood Avatar asked Feb 19 '23 23:02

John Threepwood


1 Answers

You get the above error because Int is not a supertype of String.

Note that in the ListNode code above, String is a supertype of Null (see class hierarchy), and Any is a supertype of String (as you rightly pointed out).

I would guess the confusions is caused by comparing two operations that are not really the same: new E[Int] instantiates the class with the type parameter Int, which is not conformant to the lower bound String and hence fails.

In the ListNode code above, on the other hand, you call the prepend method that takes a supertype U of T. When creating anyList, U is (just as you guessed) resolved to Any since this is the only common supertype of String and Int, so you could think of it as not really passing an Int to it, but just some arbitrary instance of Any (which also happens to be of type Int).

Therefore,

val anyList: ListNode[Int] = strList.prepend(12345)

fails as well, as strList.prepend(12345) can only return ListNode[Any].

like image 60
Roland Ewald Avatar answered May 10 '23 01:05

Roland Ewald