Define a type constraint with abstract type

359 views Asked by At

I try to define a type constraint with abstract type. but unfortunately, it doesn't compile.

  sealed trait MatchableValue {
    type A
    def value: A

    def asSingleItemValue : ItemValue
  }

  sealed trait ItemValue {
    type A
    def value: A
  }

  case class StringValue(value: String) extends ItemValue {type A = String}
  case class StringMatchableValue(value: String) extends MatchableValue{
    type A = String
    override def asSingleItemValue =  StringValue(value)
  }

Unfortunately, this one doesn't work

def asSingleItemValue[B <: ItemValue](implicit ev: A =:= B#A) : B

The aim of the type constraint is to be warned at compile-time of such an error :

  case class IntValue(value: Int) extends ItemValue {type A = Int}
  case class IntMatchableValue(value: Int) extends MatchableValue{
    type A = Int
    def asSingleItemValue = StringValue("error")
  }
2

There are 2 answers

0
Travis Brown On BEST ANSWER

You can accomplish this with a type refinement (note the method's return type):

sealed trait MatchableValue { self =>
  type A
  def value: A

  def asSingleItemValue: ItemValue { type A = self.A }
}

sealed trait ItemValue {
  type A
  def value: A
}

case class StringValue(value: String) extends ItemValue { type A = String }
case class IntValue(value: Int) extends ItemValue { type A = Int }

Now this compiles:

case class StringMatchableValue(value: String) extends MatchableValue {
  type A = String
  def asSingleItemValue = StringValue(value)
}

But this doesn't:

case class StringMatchableValue(value: String) extends MatchableValue {
  type A = String
  def asSingleItemValue = IntValue(1)
}

Which I believe is what you want.


It's also worth noting that the following is a common pattern when dealing with type refinements:

sealed trait MatchableValue { self =>
  type A
  def value: A

  def asSingleItemValue: ItemValue.Aux[A]
}

sealed trait ItemValue {
  type A
  def value: A
}

object ItemValue {
  type Aux[A0] = ItemValue { type A = A0 }
}

This does exactly the same thing, but the syntax is a nice alternative if you find yourself needing to write out the type refinement a lot.

1
bjfletcher On

I think you meant to delete this line in your code?

def asSingleItemValue = StringValue(value)

and the override definition has some of the signature missing and should be like this:

override def asSingleItemValue[B <: ItemValue](implicit ev: =:=[String, B#A]): B = ev

Finally, the resulting type of ev needs to be B.