Scala 中的类型级模式匹配

type level pattern matching in scala

理想情况下,我想在 Scala 的类型级别上编写 Haskell 样式模式匹配,如下所示:

shapeless 可以用于这样的事情吗?

object Test{


  type F[Int] = String
  type F[Boolean] = Int  // but this line does not compile

  implicitly[String =:= F[Int]]
  implicitly[Int =:= F[Boolean]]

}

在这个例子中,如果 F 需要 Int 那么它 returns String 如果它需要 Boolean 那么它 returns Int.

澄清(基于

下面是我想在函数和类型类中使用这些类型的方式:

  abstract class WrappedF[T] {
    type F
    type Unwrap = T
  }

  type F[X <: WrappedF[_]] = X#F

  class IntF      extends WrappedF[Int]     { type F = StringF }
  class BooleanF  extends WrappedF[Boolean] { type F = IntF }
  class StringF   extends WrappedF[String]  { type F = Nothing }

  implicitly[String =:= F[IntF]#Unwrap]
  implicitly[Int =:=    F[BooleanF]#Unwrap]
  implicitly[String =:= F[F[BooleanF]]#Unwrap]

  // this is a type class definition where `V` is a member of the `Test` class
  // `f`'s type should be defined by `V`, but it does not work :(


  trait Test[V <: WrappedF[V]]{
    def f(a: F[V]#Unwrap): F[V]#Unwrap // this does not compile
  }

  implicit object TestImpl extends Test[IntF]{
    override def f(a: F[IntF]#Unwrap): F[IntF]#Unwrap = {
      val z: F[IntF]#Unwrap = "fd"+a
      z
    }
  }

您使用带有类型成员的类型类样式隐式结构。

// sealed for closed family
class F[I] { type O }
object F {
  type Rel[I, O0] = F[I] { type O = O0 }
  /* private */ def mkF[I, O0]: Rel[I, O0] = new F[I] { override type O = O0 }
  implicit val fInt: Rel[Int, String] = mkF
  implicit val fBoolean: Rel[Boolean, Int] = mkF
  def apply[I](implicit f: F[I]): f.type = f // f.type survives the refinement (see what breaks on removal)
}

locally { val temp = F[Int]; implicitly[temp.O =:= String] }
locally { val temp = F[Boolean]; implicitly[temp.O =:= Int] }
locally { val temp0 = F[Boolean]; val temp1 = F[temp0.O]; implicitly[temp1.O =:= String] }

这里有两个非无形解。

  1. 一些类型级别 table 的具有有趣名称的常量:

    type F = {
      type Int = java.lang.String
      type Boolean = scala.Int
    }
    
    implicitly[String =:= F#Int]
    implicitly[Int =:= F#Boolean]
    

    这里,F是一个有两个类型成员的类型,它们的名字分别是IntBoolean(可能是IB,这两个常量与 IntBoolean 没有任何关系)。这不构成:你不能写下像 F#F#Int.

  2. 这样的东西
  3. 您可以将要为其定义 F 的每个类型 T 提升为同时具有 TF[T] 类型的类型成员:

    abstract class WrappedF[T] {
      type F
      type Unwrap = T
    }
    type F[X <: WrappedF[_]] = X#F
    class IntF extends WrappedF[Int] { type F = StringF }
    class BooleanF extends WrappedF[Boolean] { type F = IntF }
    class StringF extends WrappedF[String] { type F = Nothing }
    
    implicitly[String =:= F[IntF]#Unwrap]
    implicitly[Int =:= F[BooleanF]#Unwrap]
    implicitly[String =:= F[F[BooleanF]]#Unwrap]
    

    由于 ...F#Unwrap,这增加了更多噪音,但这纯粹是类型级计算,并且它组合(如最后一个示例所示)。


更新(更适合对 V <: WrappedF 进行抽象)

在 "Clarification" 下的代码中,您缺少绑定到类型成员 F:

F <: WrappedF
  abstract class WrappedF {
    type F <: WrappedF
    type Unwrap
  }

  type F[X <: WrappedF] = X#F

  class IntF      extends WrappedF { type Unwrap = Int;     type F = StringF }
  class BooleanF  extends WrappedF { type Unwrap = Boolean; type F = IntF }
  class StringF   extends WrappedF { type Unwrap = String;  type F = Nothing }

  implicitly[String =:= F[IntF]#Unwrap]
  implicitly[Int    =:= F[BooleanF]#Unwrap]
  implicitly[String =:= F[F[BooleanF]]#Unwrap]

  trait Test[V <: WrappedF]{
    def f(a: F[V]#Unwrap): F[V]#Unwrap // this does not compile
  }

  implicit object TestImpl extends Test[IntF] {
    override def f(a: String): String = {
      val z: F[IntF]#Unwrap = "fd" + a
      z
    }
  }