我有
F ~> H
G ~> H
式中~>
为cats.NaturalTransformation
。
λ[A => F[A] :+: G[A] :+: CNil] ~> H
使用kind-projector语法提高可读性
我是这样做的
def or[G[_]](g: G ~> H): λ[A => F[A] :+: G[A] :+: CNil] ~> H =
new (λ[A => F[A] :+: G[A] :+: CNil] ~> H) {
def apply[A](fa: F[A] :+: G[A] :+: CNil): H[A] =
(fa.select[F[A]], fa.select[G[A]]) match {
case (Some(ff), None) => f(ff)
case (None, Some(gg)) => g(gg)
// this can't happen, due to the definition of Coproduct
case _ => throw new Exception("Something is wrong")
}
}
这是可行的,尽管我愿意听取建议,因为它看起来不太漂亮。
如果我有
λ[A => F[A] :+: G[A] :+: CNil] ~> H
K ~> H
我也应该能够构造一个
λ[A => F[A] :+: G[A] :+: K[A] :+: CNil] ~> H
这就是我被困住的地方。我尝试使用ExtendRight
从无形,但我不能得到它的工作。这是我的尝试:
def or[F[_] <: Coproduct, G[_], H[_], FG[_] <: Coproduct](f: F ~> H, g: G ~> H)(
implicit e: ExtendRight.Aux[F[_], G[_], FG[_]]
): FG ~> H = new (FG ~> H) {
def apply[A](fg: FG[A])(implicit
sf: Selector[FG[A], F[A]],
sg: Selector[FG[A], G[A]]
): H[A] =
(fg.select[F[A]], fg.select[G[A]]) match {
case (Some(ff), None) => f(ff)
case (None, Some(gg)) => g(gg)
// this can't happen, due to the definition of Coproduct
case _ => throw new Exception("Something is wrong")
}
}
但是编译器找不到ExtendRight
参数的隐式证据。
这里有一个可以玩的MWE
import shapeless._
import shapeless.ops.coproduct._
import cats.~>
object Bar {
val optionToList = new (Option ~> List) {
def apply[A](x: Option[A]): List[A] = x match {
case None => Nil
case Some(a) => List(a)
}
}
val idToList = new (Id ~> List) {
def apply[A](x: Id[A]): List[A] = List(x)
}
val tryToList = new (scala.util.Try ~> List) {
def apply[A](x: scala.util.Try[A]): List[A] = x match {
case scala.util.Failure(_) => Nil
case scala.util.Success(a) => List(a)
}
}
type OI[A] = Option[A] :+: Id[A] :+: CNil
val optionAndId: OI ~> List = Foo.or(optionToList, idToList)
val all = Foo.or2(optionAndId, tryToList)
}
object Foo {
def or[F[_], G[_], H[_]](f: F ~> H, g: G ~> H): λ[A => F[A] :+: G[A] :+: CNil] ~> H =
new (λ[A => F[A] :+: G[A] :+: CNil] ~> H) {
def apply[A](fa: F[A] :+: G[A] :+: CNil): H[A] =
(fa.select[F[A]], fa.select[G[A]]) match {
case (Some(ff), None) => f(ff)
case (None, Some(gg)) => g(gg)
// this can't happen, due to the definition of Coproduct
case _ => throw new Exception("Something is wrong, most likely in the type system")
}
}
def or2[F[_] <: Coproduct, G[_], H[_], FG[_] <: Coproduct](f: F ~> H, g: G ~> H)(implicit
e: ExtendRight.Aux[F[_], G[_], FG[_]]
): FG ~> H = new (FG ~> H) {
def apply[A](fg: FG[A])(implicit
sf: Selector[FG[A], F[A]],
sg: Selector[FG[A], G[A]]
): H[A] =
(fg.select[F[A]], fg.select[G[A]]) match {
case (Some(ff), None) => f(ff)
case (None, Some(gg)) => g(gg)
// this can't happen, due to the definition of Coproduct
case _ => throw new Exception("Something is wrong, most likely in the type system")
}
}
}
对不起,我没有抽出时间来张贴这几天,但我认为它是你想要的。这里的技巧是用一种你甚至不需要选择器的方式来安排事情。
import shapeless._
import shapeless.ops.coproduct._
import cats.~>
def or[F[_], G[_], H[_]](
f: F ~> H,
g: G ~> H
): ({ type L[x] = F[x] :+: G[x] :+: CNil })#L ~> H =
new (({ type L[x] = F[x] :+: G[x] :+: CNil })#L ~> H) {
object fg extends Poly1 {
implicit def atF[A]: Case.Aux[F[A], H[A]] = at(f(_))
implicit def atG[A]: Case.Aux[G[A], H[A]] = at(g(_))
}
def apply[A](c: F[A] :+: G[A] :+: CNil): H[A] = c.fold(fg)
}
def or2[F[_], G[_] <: Coproduct, H[_]](
f: F ~> H,
g: G ~> H
): ({ type L[x] = F[x] :+: G[x] })#L ~> H =
new (({ type L[x] = F[x] :+: G[x] })#L ~> H) {
def apply[A](c: F[A] :+: G[A]): H[A] = c match {
case Inl(fa) => f(fa)
case Inr(ga) => g(ga)
}
}
(请注意,我在or
中使用Poly1
,以避免需要处理CNil
的异常情况。)
现在你可以这样写:
val optionToList = new (Option ~> List) {
def apply[A](x: Option[A]): List[A] = x.fold[List[A]](Nil)(List(_))
}
val idToList = new (Id ~> List) {
def apply[A](x: Id[A]): List[A] = List(x)
}
val tryToList = new (scala.util.Try ~> List) {
def apply[A](x: scala.util.Try[A]): List[A] = x match {
case scala.util.Failure(_) => Nil
case scala.util.Success(a) => List(a)
}
}
然后:
scala> type OI[A] = Option[A] :+: Id[A] :+: CNil
defined type alias OI
scala> val optionAndId: OI ~> List = or(optionToList, idToList)
optionAndId: cats.~>[OI,List] = $anon$1@55224c4a
scala> val all = or2(tryToList, optionAndId)
all: cats.~>[[x]shapeless.:+:[scala.util.Try[x],OI[x]],List] = $anon$2@536a993
scala> all(Inl(scala.util.Try('foo)))
res8: List[Symbol] = List('foo)
scala> all(Inr(Inl(Option('foo))))
res9: List[Symbol] = List('foo)
scala> all(Inr(Inr(Inl('foo))))
res10: List[Symbol] = List('foo)
(如果你不介意写出类型,当然也可以使用Coproduct[...](Option('foo))
等)