5

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?

Artur Eshenbrener
  • 1,850
  • 2
  • 17
  • 24

2 Answers2

6

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.

senia
  • 37,745
  • 4
  • 88
  • 129
  • Thank you, it is working. Could you give me a documentation link to read more about this technique? – Artur Eshenbrener Dec 08 '14 at 09:44
  • @ArturEshenbrener: unfortunately I can't. I guess I saw this kind of code in answers of [Miles Sabin](http://stackoverflow.com/users/146737/miles-sabin?tab=answers), though I'm not sure. – senia Dec 08 '14 at 09:50
1

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.

Community
  • 1
  • 1
  • @senia You are right. In fact, it suffers from the Scala's limitation highlighted at http://stackoverflow.com/a/6690611/1893995 But I'd like to keep the answer precisely for exposing that problem. – Pablo Francisco Pérez Hidalgo Dec 08 '14 at 10:12
  • It's unsafe and will fail at runtime instead of compile time on `SetOptionMessage(SomeBooleanOption)(Some("a"))`. You should not use class cast at all. And you don't have to. See `Different parameter groups` from my answer. – senia Dec 08 '14 at 10:15
  • It's not about `scala`'s limitations. It's about unnecessary class cast. – senia Dec 08 '14 at 10:16
  • @senia The Scala's limitation is that, at runtime, the code `val msg = SetOptionMessage(SomeBooleanOption, Some("hello"))` does not throw an exception. I agree with your comments on the hazards of using cast. – Pablo Francisco Pérez Hidalgo Dec 08 '14 at 10:17