Home > Blockchain >  Error with type bounds in function definition
Error with type bounds in function definition

Time:02-17

I have some class hierarchy:

trait Params
trait ParamsWithName extends Params {
  val name: String
}
case class ParamsWithNameAndValue(name: String, value: String) extends ParamsWithName

I want to implement a few classes that will use these. The following works:

trait Worker[T <: ParamsWithName] {
  def work(parameters: T): String = parameters.name
}

class SimpleWorker extends Worker[ParamsWithNameAndValue] {
  override def work(parameters: ParamsWithNameAndValue): String = s"${parameters.name}   ${parameters.value}"
}

The following does not:

trait Worker {
  def work[T <: ParamsWithName](parameters: T): String = parameters.name
}

class SimpleWorker extends Worker {
  override def work[ParamsWithNameAndValue](parameters: ParamsWithNameAndValue): String = s"${parameters.name}   ${parameters.value}"
}

The error is:

error: value name is not a member of type parameter ParamsWithNameAndValue
  override def work[ParamsWithNameAndValue](parameters: ParamsWithNameAndValue): String = s"${parameters.name}   ${parameters.value}"
error: value value is not a member of type parameter ParamsWithNameAndValue
  override def work[ParamsWithNameAndValue](parameters: ParamsWithNameAndValue): String = s"${parameters.name}   ${parameters.value}"

I would like to understand why this is the case.

Scala version is 2.12.

CodePudding user response:

This:

trait Worker[T <: ParamsWithName] {
  def work(parameters: T): String = parameters.name
}

Means that the Worker type itself is parametric on T (which must be a subtype of ParamsWithName). Which implies that a concrete instance of Worker will only work for one such concrete type for T

Thus, here:

class SimpleWorker extends Worker[ParamsWithNameAndValue] {

You are saying that the SimpleWorker type is a subtype of Worker[ParamsWithNameAndValue] meaning that all instances of SimpleWorker are instances of Worker[ParamsWithNameAndValue]


Whereas, this:

trait Worker {
  def work[T <: ParamsWithName](parameters: T): String = parameters.name
}

Means that any instance of Worker has a method work which is parametric on T (which must be a subtype of ParamsWithName). Implying that any Worker must be able to handle any possible type T

Thus, here:

class SimpleWorker extends Worker {
  override def work[ParamsWithNameAndValue](parameters: ParamsWithNameAndValue): String = s"${parameters.name}   ${parameters.value}"
}

You are not only having a syntax error, but a conceptual problem.
Because you want to say that the work method on SimpleWorker can only accept ParamsWithNameAndValue but that would be a violation of the Liskov substitution principle.


In conclusion, the first approach is the one that encodes the intention you want.

  • Related