Search code examples
scalapattern-matchinghigher-kinded-types

Pattern matching higher-kinded parameter


I have a domain model with an ADT with 4 subcases, e.g.

sealed trait Param

case class A(...) extends Param
case class B(...) extends Param
case class C(...) extends Param
case class D(...) extends Param

And various type parameterised on this Param, e.g.

case class Config[T <: Param](...)
case class Parser[T <: Param](...)
case class Price[T <: Param](...)

I would love to be able to pattern match each of those:

val config: Config[T] = ??? 
//Assuming T <: Param, as defined above
config match {
  case confA: Config[A] => 
  case confB: Config[B] => 
  case confC: Config[C] => 
  case confD: Config[D] => 
}

But as we know type erasure makes this a pain...

Do notice though that I always need to match a higher-kinded F[T <: Param].

Any neat ideas? I have seen shapeless typeable, but not sure it how I could use it in my case. Switching to typelevel scala (which I think solves type parameter matching, woohoo) is not an option.

Any neat ideas people? :D

EDIT: Here is an approach I tried but run into compiler issues:

sealed trait Param

case class A() extends Param 
case class B() extends Param
case class C() extends Param
case class D() extends Param

sealed trait ForParam[F[T <: Param], T <:Param] {
  def value: F[T]
}
case class ForParamA[F[_]](value: F[A]) extends ForParam[F, A]
case class ForParamB[F[_]](value: F[B]) extends ForParam[F, B]
case class ForParamC[F[_]](value: F[C]) extends ForParam[F, C]
case class ForParamD[F[_]](value: F[D]) extends ForParam[F, D]

object ForParam {
  case class Example[T <: Param](f: T => String)

  val exampleFormParam: ForParam[Example, _] = ???
  //The below does not compile:
  //  [error]      constructor cannot be instantiated to expected type;
  //  [error]       found   : ForParamD[F]
  //  [error]       required: ForParam[ForParam.Example,_$2] where type _$2
//  I think I run into issue related to https://github.com/scala/scala/pull/6069
  exampleFormParam match {
    case ForParamA(value) =>
    case ForParamB(value) =>
    case ForParamC(value) =>
    case ForParamD(value) =>
  }

}

Solution

  • You lost bound for type parameter in ForParamA etc.

    The following code compiles:

      sealed trait Param
    
      case class A() extends Param
      case class B() extends Param
      case class C() extends Param
      case class D() extends Param
    
      sealed trait ForParam[F[_ <: Param], T <: Param] {
        def value: F[T]
      }
      case class ForParamA[F[_ <: Param]](value: F[A]) extends ForParam[F, A]
      case class ForParamB[F[_ <: Param]](value: F[B]) extends ForParam[F, B]
      case class ForParamC[F[_ <: Param]](value: F[C]) extends ForParam[F, C]
      case class ForParamD[F[_ <: Param]](value: F[D]) extends ForParam[F, D]
    
      object ForParam {
        case class Example[T <: Param](f: T => String)
    
        val exampleFormParam: ForParam[Example, _] = ???
        exampleFormParam match {
          case ForParamA(value) => ???
          case ForParamB(value) => ???
          case ForParamC(value) => ???
          case ForParamD(value) => ???
        }
    
      }