扩展副产品的自然转化
Extend NaturalTransformation of Coproducts
我有
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
这就是我卡住的地方。我尝试使用 shapeless 中的 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@55224c4a
scala> val all = or2(tryToList, optionAndId)
all: cats.~>[[x]shapeless.:+:[scala.util.Try[x],OI[x]],List] = $anon@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))
等。)
我有
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
这就是我卡住的地方。我尝试使用 shapeless 中的 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@55224c4a
scala> val all = or2(tryToList, optionAndId)
all: cats.~>[[x]shapeless.:+:[scala.util.Try[x],OI[x]],List] = $anon@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))
等。)