如何自动生成一个函数来匹配一个带有隐式实例的密封案例类族?

时间:2017-09-06 21:54:20

标签: scala functional-programming shapeless

我有一个密封的案例类系列,它指定了一些规则,这些规则是从外部源反序列化的。我还有一个类型类,有几个实例来执行实际逻辑,如:

import scala.util.Try

sealed trait ReaderConfig
case class Substring(begin: Int, end: Int) extends ReaderConfig
case class Regex(expr: String) extends ReaderConfig

trait Read[M[_], RC <: ReaderConfig] {
  def apply(config: RC, raw: String): M[String]
}

object Read {
  implicit val TryReadSubstring: Read[Try, Substring] = (config: Substring, raw: String) => Try {
    raw.substring(config.begin, config.end)
  }
  implicit val TryReadRegex: Read[Try, Regex] = (config: Regex, raw: String) => Try {
    config.expr.r.findFirstIn(raw).get
  }

  trait Helper[RC <: ReaderConfig] {
    def as[M[_]](implicit read: Read[M, RC]): M[String]
  }

  def apply[RC <: ReaderConfig](config: RC)(raw: String) = new Helper[RC] {
    override def as[M[_]](implicit read: Read[M, RC]): M[String] = read.apply(config,raw)
  }
}

现在,在将它与具体类型一起使用时,找到正确的隐式没有问题。

@ val ok: Try[String] = Read(Substring(0,1))("abc").as[Try]
ok: Try[String] = Success("a")
@ val Fail: Try[String] = Read(Substring(1000,9001))("abc").as[Try]
Fail: Try[String] = Failure(
  java.lang.StringIndexOutOfBoundsException: String index out of range: 9001
)

当我有一个具有上特征类型的val时(比如我上面提到的反序列化),它无法按预期编译:

@ val config: ReaderConfig = Substring(0,1)
config: ReaderConfig = Substring(0, 1)
@ val fail2: Try[String] = Read(config)("abc").as[Try]
cmd8.sc:1: could not find implicit value for parameter read: $sess.cmd2.Read[scala.util.Try,$sess.cmd1.ReaderConfig]
val fail2: Try[String] = Read(config)("abc").as[Try]
                                               ^
Compilation Failed

我提出的唯一解决方案是编写一个函数,将实际类型与正确的实例相匹配,例如:

val tryRead: ReaderConfig => String => Try[String] = {rc => raw => rc match {
  case s: Substring => Read[Substring](s)(raw).as[Try]
  case r: Regex => Read[Regex](r)(raw).as[Try]
}}

然后它很乐意编译,我可以在那种情况下使用它。

@ tryRead(config)("abc")

res9: Try[String] = Success("a")

这个特性是密封的,所以编译器应该警告我丢失case s,但很明显,只要我有更多这样的实例,它就会变得非常麻烦。

是否有某种方法可以自动生成此功能? 毕竟,通过复制和粘贴case并只填写变量模式可以创建一些东西。

1 个答案:

答案 0 :(得分:2)

我认为更常见的模式是为超类型(ReaderConfig)创建单个类型类实例,而不是每个子类型创建一个实例。然后使用智能构造函数创建ReaderConfig。例如,Cats为some提供noneOption个构造函数:42.some返回Option[Int]而不是Some[Int]