2
votes

What is wrong with the code below? I'm getting the following complaint from the compiler on the indicated line: type arguments [Asset] do not conform to trait Worker's type parameter bounds [T <: br.Doable]

How is this so? Worker expects a subtype of Doable, and asset extends Doable.

trait Doable

trait Worker[T<:Doable] {
  def hey():String
}

case class Asset() extends Doable

case class Hey[Asset] extends Worker[Asset] { // << error here
  def hey() = "You!"
}
3
Type parameter in the case class declaration messes things up, case class Hey() extends Worker[Asset] { def hey() = "You!" } works just fine. As to why, no idea, although I haven't really put much thought to it, honestly :)Patryk Ćwiek
case class Hey[Asset] extends Worker[Asset] and case class Hey[T] extends Worker[T] means the same. Asset here is just a new name for type parameter of class Hey. See stackoverflow.com/a/19137992/406435senia

3 Answers

6
votes

When you declare case class Hey[Asset], you bind a new type variable Asset, you do not refer to case class Asset() extends Doable (you are shadowing the Asset type variable). Your code is equivalent to :

  case class Hey[A] extends Worker[A] {
    ...
  }

which obviously won't work.

2
votes

The problem is you have confused yourself by using the same value, Asset, to refer to a case class and a type parameter.

You probably intend to do something like this:

case class Hey[T](str: String) extends Worker[Asset] { 
  def hey() = "You!"
}

Though it is beside the point, note that I added a parameter to Hey because case classes without parameters have been deprecated.

0
votes

This has been asked a lot of times, and I think that the confusion can easily go away if you see the analogy between type parameters and constructor parameters, thinking about them just as different kinds of constructor parameters: type-level and value-level.

Disclaimer Of course, this is only an analogy and it will break at a lot of different levels and there are a lot of corner cases as with anything Scala; but my point here is that it can be useful

At the type level, you can think of <: as a the equivalent of : at the value level:

class TypeParamsVsVals {

  type X
  type X1 <: X

  class Buh[T <: X]
  class Oh[T1 <: X1] extends Buh[T1]
  // wait for Scala 3
  // class Oh[T1 <: X1] extends Buh[T = T1]

  type x
  type x1 <: x

  class buh(val t: x)
  class oh(val t1: x1) extends buh(t = t1)
}  

What I think is the main source of confusion is that at the type level there's no kind distinction between the two sides of <:, and to make things worse you can write T without any (no pun intended) bound, while you cannot do the same at the value level:

class NoBounds[T]
// same as 
class AltNoBounds[T <: Any]

// you cannot write
// class noBounds(val t)
class noBounds(val t: Any)