我正在尝试制作这款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是否是正确的方法,我需要的是以某种方式找到具体的实例类型而不明确地匹配它或类似的东西
答案 0 :(得分:1)
这可能比您预期的要长,但我认为这应该是它的样子。
因此,在方法泛型中,我们得到SqlArgument
的通用表示,它只是SqlArgument
的一个具体子类型的实例。在您的情况下LanguageCode :+: BlobHashs :+: LastSeenId :+: CNil
。为了确定我们的实例实际上是哪种类型,我们将该通用表示传递给implicit
提供的coproduct
值,并以头尾方式递归递送。
在您的情况下H
= LanguageCode
和T
= 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)
}
}