确定没有明确匹配的具体类型

时间:2017-01-28 11:48:36

标签: scala generic-programming shapeless

我正在尝试制作这款DRYer:

type CollectPredicate = PartialFunction[Option[SqlArgument], SQLActionBuilder]

val cp: CollectPredicate = {
    case Some(lc: LanguageCode) => buildSql(lc)
    case Some(bh: BlobHashs) => buildSql(bh)
    case Some(lsi: LastSeenId) => buildSql(lsi)
  }

我想找到一种方法来做类似的事情:

type args = LanguageCode :+:  BlobHashs :+: LastSeenId :+: CNil

val PlacesRequestType = Coproduct[args]

val cp  =  (sq: args) => buildSql(PlacesRequestType(sql))

这是buildSql:

def buildSql[T, A](args: T)(implicit sqlArgument: SqlArgumentBuilder[T, A], sp: SetParameter[A]): SQLActionBuilder = {
    sqlArgument.sql(args)
  }

我不确定coproduct是否是正确的方法,我需要的是以某种方式找到具体的实例类型而不明确地匹配它或类似的东西

1 个答案:

答案 0 :(得分:1)

这可能比您预期的要长,但我认为这应该是它的样子。

因此,在方法泛型中,我们得到SqlArgument的通用表示,它只是SqlArgument的一个具体子类型的实例。在您的情况下LanguageCode :+: BlobHashs :+: LastSeenId :+: CNil。为了确定我们的实例实际上是哪种类型,我们将该通用表示传递给implicit提供的coproduct值,并以头尾方式递归递送。

在您的情况下H = LanguageCodeT = BlobHashs :+: LastSeenId :+: CNil。 如果我们的实例是LanguageCode类型,它将与第一种情况匹配。 如果它是另一种类型,它将以尾部递归地进行。这是H = BlobHashs和T = LastSeenId :+: CNil等等....

import shapeless.{Generic, Coproduct, Inr, Inl, CNil, :+:}

trait cp[T] {
  def apply(t: T): SQLActionBuilder
}

object cp {
  def apply(a: SqlArgument)(implicit cp: cp[SqlArgument]) = cp(a)

  def pure[T](f: T => SQLActionBuilder) = new cp[T] {
    def apply(t: T) = f(t)
  }

  implicit def generic[Co <: Coproduct](implicit gen: Generic.Aux[SqlArgument, Co], cp: cp[Co]): cp[SqlArgument] = pure {
    sql => cp(gen.to(sql))
  }

  implicit val cnil: cp[CNil] = pure(_ => throw new Exception("Impossible!"))

  implicit def coproduct[H, T <: Coproduct, A](implicit cp: cp[T],
                                                        b: SqlArgumentBuilder[H, A],
                                                        sp: SetParameter[A]): cp[H:+:T]  = pure {
    case Inl(h) => buildSql(h)
    case Inr(t) => cp(t)
  }
}