scala中的类型级别模式匹配

时间:2018-06-17 11:06:17

标签: scala types pattern-matching shapeless type-level-computation

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

可以无形地用于这样的事情吗?

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,则会返回String,如果需要Boolean,则返回Int

澄清(基于this answer

以下是我希望在函数和类型类中使用这些类型的方法:

  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
    }
  }

2 个答案:

答案 0 :(得分:7)

这里有两个非无形的解决方案。

  1. 一个带有趣名字的小型常量表:

    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. 您可以将要定义T的每个类型F提升为同时包含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,这会增加更多噪音,但纯粹是一种类型级别的计算,并且它组成(如最后一个示例所示)。

  4. 更新(更适合抽象V <: WrappedF

    在“澄清”下的代码中,您错过了F <: WrappedF类型成员F上的约束:

      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
        }
      }
    

答案 1 :(得分:5)

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

// 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] }