用于密封性状的穷尽型匹配

时间:2017-10-23 16:05:05

标签: scala

我希望能够详尽地匹配密封特性的实现类型,如下例所示。理想情况下,我希望尽可能避免使用反射(TypeTags)和隐式转换。有没有办法彻底匹配密封特性的类型?

object DataTypes {

  sealed trait StrFy {
    def stringify: String
  }

  final case class StrFyString(s: String) extends StrFy {
    def stringify = s
  }

  final case class StrFyInt(i: Int) extends StrFy {
    def stringify = i.toString
  }

  def stringifyThings[T <: StrFy](values: T*): String = {
    val label = T match {
      case StrFyString => "string"
      case StrFyInt => "integer"
      // cases that don't extend StrFy cause a compile error
    }

    "The " + label + " values are: " + values.map(_.stringify.fold("")(_+", "+_))
  }

  def printStringified(): Unit = {
    println(stringifyThings(StrFyString("foo"), StrFyString("bar"))) // should print: "the string values are: foo, bar"
    println(stringifyThings(StrFyInt(1), StrFyInt(2), StrFyInt(3))) // should print: "the integer values are: 1, 2, 3"
  }
}

1 个答案:

答案 0 :(得分:2)

除非您使用由“无暗示”排除的类型类,否则无法获取给定类型的值。因此,您需要匹配实例。

object DataTypes extends App {

  sealed trait StrFy {
    def stringify: String
  }

  final case class StrFyString(s: String) extends StrFy {
    def stringify = s
  }

  final case class StrFyInt(i: Int) extends StrFy {
    def stringify = i.toString
  }

  def stringifyThings[T <: StrFy](values: T*): String = {
    def label(value: T) = value match {
      case _:StrFyString => "string"
      case _:StrFyInt => "integer"
      // cases that don't extend StrFy cause a compile error
    }
    // Will throw if values is empty
    "The " + label(values.head) + " values are: " + values.map(_.stringify).mkString(", ")
  }

  def printStringified(): Unit = {
    println(stringifyThings(StrFyString("foo"), StrFyString("bar"))) // should print: "the string values are: foo, bar"
    println(stringifyThings(StrFyInt(1), StrFyInt(2), StrFyInt(3))) // should print: "the integer values are: 1, 2, 3"
  }
  printStringified()
}

Implicits并不那么可怕:)看看:

object DataTypes extends App {

  sealed trait StrFy[T] {
    def stringify(value: T): String
    def label: String
  }

  implicit object StrFyString extends StrFy[String] {
    override def stringify(value: String): String = value
    override def label: String = "string"
  }
  implicit object StrFyInt extends StrFy[Int] {
    override def stringify(value: Int): String = value.toString
    override def label: String = "integer"
  }

  def stringifyThings[T: StrFy](values: T*): String = {
    val strFy = implicitly[StrFy[T]]
    // Safe even on empty values
    "The " + strFy.label + " values are: " + values.map(strFy.stringify).mkString(", ")
  }

  def printStringified(): Unit = {
    println(stringifyThings("foo", "bar")) // should print: "the string values are: foo, bar"
    println(stringifyThings(1, 2, 3)) // should print: "the integer values are: 1, 2, 3"
  }
  printStringified()
}

编译器为您执行“模式匹配”,在给定输入类型的情况下提供正确的实例。 您会看到类型类允许您获取一个值label,只给出一个类型。这是一个非常基本的概念,使类型类多态性强于子类型:)