Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: refer to nested type of class, which comes as type parameter to generic

Tags:

scala

I want to implement class, which describes action about setting some option value. I want to make option 's value type be depent of option type. I am trying to make this:

case class SetOptionMessage[T <: BaseOptionType](
    option: T
    value: Option[T#ValueType]
)

abstract class BaseOptionType {
    type ValueType
}


object SomeBooleanOption extends BaseOptionType {
    final type ValueType = Boolean
}

But when I am trying to use that classes like this:

val msg = SetOptionMessage(SomeBooleanOption, Some(true))

I got compilation error:

Error:(15, 43) type mismatch;
found   : Some[Boolean]
required: Option[?#ValueType]
SetOptionMessage(SomeBooleanOption, Some(true))
                                      ^

How can I proper refer to nested type?

like image 363
Artur Eshenbrener Avatar asked Dec 08 '14 09:12

Artur Eshenbrener


2 Answers

Type member

case class SetOptionMessage[V, T <: BaseOptionType{ type ValueType = V }](
  option: T,
  value: Option[V]
)

val msg = SetOptionMessage(SomeBooleanOption, Some(true))
// msg: SetOptionMessage[Boolean,SomeBooleanOption.type] = SetOptionMessage(SomeBooleanOption$@2e93ebe0,Some(true))

Implicit evidence

case class SetOptionMessage[V, T <: BaseOptionType](
  option: T,
  value: Option[V])(implicit e: V =:= T#ValueType)

SetOptionMessage(SomeBooleanOption, Some(true))
// SetOptionMessage[Boolean,SomeBooleanOption.type] = SetOptionMessage(SomeBooleanOption$@2e93ebe0,Some(true))

SetOptionMessage(SomeBooleanOption, None)
// SetOptionMessage[SomeBooleanOption.ValueType,SomeBooleanOption.type] = SetOptionMessage(SomeBooleanOption$@2e93ebe0,None)

Different parameter groups

You can't use case class with this solution.

class SetOptionMessage[T <: BaseOptionType](option: T)(val value: Option[T#ValueType])

val msg = new SetOptionMessage(SomeBooleanOption)(Some(true))
// SetOptionMessage[SomeBooleanOption.type] = SetOptionMessage@7f216e0c

msg.value
// Option[SomeBooleanOption.ValueType] = Some(true)

Unsafe class cast

Don't use it.

like image 161
senia Avatar answered Oct 12 '22 15:10

senia


ValueType is a path-dependent type. That means you can't access it using #, which can only refer to non path-dependent types.

Try changing SetOptionMessage to:

case class SetOptionMessage[T <: BaseOptionType](val option: T,
                                                 private val _value: Option[Any]) {
  val value = _value.asInstanceOf[Option[option.ValueType]]
}

However, you should not use this solution (check note below). I am keeping the answer undeleted to expose its problems.

NOTE

As @senia indicates in his answer, this is the worst solution since it relies on class cast. This cast presents some limitations as the highlighted in this SO answer: https://stackoverflow.com/a/6690611/1893995

The expression:

val msg = SetOptionMessage(SomeBooleanOption, Some("hello"))

will not only compile but it won't even crash at runtime.

like image 42
Pablo Francisco Pérez Hidalgo Avatar answered Oct 12 '22 14:10

Pablo Francisco Pérez Hidalgo