我想在
Option
的背景下组合两个endofunctor。我想要的组合是通过 Category.compose
将两个内函子组合成一个。我发现 MonoidK[Endo].algebra[*]
的 Semigroup[Endo[*]]
实例满足我的要求。我正在使用 Semigroup[Endo[*]]
使 Option
成为幺半群 Endo
。
这是我实现它的尝试,但它结合了两个内函数,而不是我想提供的
Semigroup[Endo[*]]
,而是通过 cats.kernel
的一些 A => B
半群实例。
import cats.*
import cats.syntax.all.*
import cats.implicits.*
val f: Endo[Int] = _ + 1
val g: Endo[Int] = _ + 2
val of = f.pure[Option]
val og = g.pure[Option]
given Semigroup[Endo[Int]] = MonoidK[Endo].algebra[Int]
val oh = Monoid[Option[Endo[Int]]].combine(of, og)
val res1 = // Some(9)
oh.mapApply(3)
val res2 = // Some(6)
Semigroup[Endo[Int]].combine(f, g).apply(3).pure[Option]
res2
是我想要的结果。我该如何解决它?
让我们添加一些调试实用程序:
// subtype of Endo[Int] which would print debug info in evaluation
case class EndoImpl(name: String, added: Int) extends (Int => Int) {
def apply(a: Int): Int = {
println(s"$name($a)==$a+$added")
a + added
}
override def compose[A](g: A => Int): A => Int = g.andThen(this)
override def andThen[A](g: Int => A): Int => A = (this, g) match {
case (f1: EndoImpl, g1: EndoImpl) =>
EndoImpl(s"(${f1.name} andThen ${g1.name})", f1.added + g1.added)
.asInstanceOf[Int => A]
case _ => super.andThen(g)
}
}
那么我们稍微修改一下示例吧
val f: Endo[Int] = EndoImpl("f", 1)
val g: Endo[Int] = EndoImpl("g", 2)
val of = f.pure[Option]
val og = g.pure[Option]
given Semigroup[Endo[Int]] = MonoidK[Endo].algebra[Int]
val oh: Option[Endo[Int]] = Monoid[Option[Endo[Int]]].combine(of, og)
println("oh.mapApply(3)")
println(
oh.mapApply(3)
)
println()
println("Semigroup[Endo[Int]].combine(f, g).apply(3).pure[Option]")
println(
Semigroup[Endo[Int]].combine(f, g).apply(3).pure[Option]
)
打印:
oh.mapApply(3)
f(3)==3+1
g(3)==3+2
Some(9)
Semigroup[Endo[Int]].combine(f, g).apply(3).pure[Option]
(g andThen f)(3)==3+3
Some(6)
有趣:
andThen
也不使用 combine
,因此它不是使用其中任何一个组合 Endo 值的幺半群combine
功能,并且 apply
让我们尝试挖掘更多。 IntelliJ 和 VC Code+metals 都有用于预览隐式传递参数或用于转换的实用程序。
他们让我们看到第二个(预期)实现大致等于:
val om: Option[Endo[Int]] = cats.kernel.instances.option.catsKernelStdMonoidForOption[Endo[Int]](
summon[Semigroup[Endo[Int]]]
).combine(of, og)
按预期工作:
(_ + 1) combine (_ + 2)
None
同时意想不到的实现是:
catsKernelStdCommutativeMonoidForOption[Endo[Int]](
catsKernelCommutativeGroupForFunction1[Int, Int]
).combine(of, og).mapApply(3)
即:
(_ + 1)(3)
,`(_ + 2)(3)Semigroup[Int]
相结合:4 + 5
这是一个隐式优先级的问题,我将通过手动构建预期实例并将其公开为
given
来解决它,而不需要进行猜测工作的优先级。