How to perform safe pattern matching on existential types in Scala 2.13?

67 Views Asked by At

Note: This is a contrived example.

Consider the following existential type.

sealed trait Showable

final case class Show[A](show: A => String, value: A) extends Showable

I can define a show method as follows:

def show(showable: Showable): String = showable match {
  case Show(show, value) => show(value)
}

But the pattern match infers the type Any => String and Any for show and value respectively. Hence, I can also define a show method as follows:

def show(showable: Showable): String = showable match {
  case Show(show, value) => show(42)
}

This is unsafe. How do I ensure that within the case expression show can only be applied to value?

2

There are 2 best solutions below

2
Dmytro Mitin On BEST ANSWER

If you match a typed pattern then

def show(showable: Showable): String = showable match {
  case s: Show[a] => s.show(s.value)
}

or

def show(showable: Showable): String = showable match {
  case s: Show[_] => s.show(s.value)
}

compiles but

def show(showable: Showable): String = showable match {
  case s: Show[a] => s.show(42)
}
//type mismatch;
// found   : Int(42)
// required: a

or

def show(showable: Showable): String = showable match {
  case s: Show[_] => s.show(42)
}
//type mismatch;
// found   : Int(42)
// required: _

doesn't.

1
tusharmath On

You could use dependant types to make it typesafe

sealed trait Showable

sealed trait Show extends Showable {
  type Inner
  def value: Inner
  def show(a: Inner): String
}

object Show {
  type Aux[A] = Show {
    type Inner = A    
  }
  
  def apply[A](f: A => String, a: A): Aux[A] = new Show {
    type Inner = A
    def value: Inner = a
    def show(a: Inner) = f(a)
  }

  def unapply(show: Show): Option[(show.Inner => String, show.Inner)] = Option(a => show.show(a), show.value)
}



def show(showable: Showable): String = showable match {
  case Show(show, value) => show(value)
}