【问题标题】:Creating a type class instance for a sealed trait为密封特征创建类型类实例
【发布时间】:2016-06-28 10:30:06
【问题描述】:

假设我有一个“宽”的密封类层次结构:

sealed trait Alphabet
case class A(word: String) extends Alphabet
...
case class Z(word: String) extends Alphabet

假设我为层次结构中的每个子类定义了一个类型类实例:

trait SwearWordFinder[T <: Alphabet] {
  def isSwearWord(x: T): Boolean
}

val swearWordFinderA = new SwearWordFinder[A] { ... }
...
val swearWordFinderZ = new SwearWordFinder[Z] { ... }

有没有一种方法可以为 Alphabet 特征本身定义一个类型类实例,而不必通过模式匹配来实现它(如下所示)?

def isSwearWord(x: Alphabet): Boolean = x match {
  case a: A => swearWordFinderA.isSwearWord(a)
  ...
  case z: Z => swearWordFinderZ.isSwearWord(z)
}

【问题讨论】:

标签: scala typeclass


【解决方案1】:

您可以将Alphabet 表示为A :+: B :+: ... :+: Z :+: CNil 的无形Coproduct,因此如果您有AB、...的SwearWordFinder 实例,并为CNil 和@987654329 定义实例@你可以使用它的通用表示获得SwearWordFinder[Alphabet]

import shapeless._

trait SwearWordFinder[T] {
  def isSwearWord(x: T): Boolean
}

object SwearWordFinder extends SwearWordFinder0 {
  implicit def apply[T](implicit swf: SwearWordFinder[T]): SwearWordFinder[T] = swf

  implicit val cnilSwearWordFinder: SwearWordFinder[CNil] = 
    new SwearWordFinder[CNil] {
      def isSwearWord(t: CNil): Boolean = false
    }

  implicit def coproductConsSwearWordFinder[L, R <: Coproduct](implicit 
    lSwf: SwearWordFinder[L], 
    rSwf: SwearWordFinder[R]
  ): SwearWordFinder[L :+: R] = 
    new SwearWordFinder[L :+: R] {
      def isSwearWord(t: L :+: R): Boolean = 
        t.eliminate(lSwf.isSwearWord, rSwf.isSwearWord)
    }
}

trait SwearWordFinder0 {
  implicit def genericSwearWordFinder[T, G](implicit 
    gen: Generic.Aux[T, G],
    swf: Lazy[SwearWordFinder[G]]
  ): SwearWordFinder[T] =
    new SwearWordFinder[T] {
      def isSwearWord(t: T): Boolean = swf.value.isSwearWord(gen.to(t))
    }
}

现在我们的信件的一些例子:

sealed trait Alphabet extends Product with Serializable

object Alphabet {
  final case class A(word: String) extends Alphabet
  final case class Z(word: String) extends Alphabet
}

implicit val swfA = new SwearWordFinder[Alphabet.A] {
  def isSwearWord(a: Alphabet.A) = a.word == "apple"
}

implicit val swfZ = new SwearWordFinder[Alphabet.Z] {
  def isSwearWord(z: Alphabet.Z) = z.word == "zebra"
}

现在我们可以得到SwearWordFinder[Alphabet]

def isBadWord[T](t: T)(implicit swfT: SwearWordFinder[T]): Boolean = 
  swfT.isSwearWord(t)

val a1: Alphabet = Alphabet.A("apple")
val z2: Alphabet = Alphabet.Z("zorro")
val z3: Alphabet = Alphabet.Z("zebra")

isBadWord(a1) // true
isBadWord(z2) // false
isBadWord(z3) // true

就像我在评论中提到的:当心SI-7046。您的Alphabet AST 需要位于具有SwearWordFinder 的项目所依赖的项目中,或者位于将在具有Alphabet 的类型类派生的包之前编译的包中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-19
    • 2015-05-26
    • 1970-01-01
    • 1970-01-01
    • 2017-09-08
    • 1970-01-01
    • 2018-10-31
    相关资源
    最近更新 更多