如何修改Endofunctor的Option实例所用的幺半群?
问题:在Option上下文中基于Category.compose组合Endofunctor
我希望在Option上下文中通过Category.compose组合两个Endofunctor,发现MonoidK[Endo].algebra[*]作为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]
问题分析
默认的Semigroup[Endo[Int]]实例(来自cats.kernel)实现的是逐点相加逻辑(即x => f(x) + g(x)),而你需要的是**函数组合(compose)**逻辑(即x => f(g(x)))。你的自定义实例没有被Monoid[Option[Endo[Int]]]正确选中,导致合成逻辑错误。
修正方案
方案1:显式使用自定义Semigroup组合Option中的Endo
放弃依赖Monoid[Option[Endo[Int]]]的默认合成逻辑,改用map2显式调用自定义Semigroup来组合值:
import cats.* import cats.syntax.all.* val f: Endo[Int] = _ + 1 val g: Endo[Int] = _ + 2 val of = f.pure[Option] val og = g.pure[Option] // 定义基于函数组合的Semigroup实例 given Semigroup[Endo[Int]] = MonoidK[Endo].algebra[Int] // 用map2显式指定合成逻辑 val oh = of.map2(og)(Semigroup[Endo[Int]].combine) val res1 = oh.mapApply(3) // 结果为Some(6),符合预期
方案2:确保自定义Semigroup优先被隐式解析
如果坚持使用Monoid[Option[Endo[Int]]].combine,可以调整代码顺序,让自定义实例在隐式解析时优先于默认实例:
import cats.* import cats.syntax.all.* // 先定义自定义Semigroup,确保隐式解析优先级 given Semigroup[Endo[Int]] = MonoidK[Endo].algebra[Int] val f: Endo[Int] = _ + 1 val g: Endo[Int] = _ + 2 val of = f.pure[Option] val og = g.pure[Option] // 现在Monoid[Option[Endo[Int]]]会使用我们的自定义Semigroup val oh = Monoid[Option[Endo[Int]]].combine(of, og) val res1 = oh.mapApply(3) // 结果为Some(6),符合预期
补充说明
MonoidK[Endo].algebra[Int]提供的Semigroup实例,其combine逻辑是函数组合(andThen):对于Endo(f)和Endo(g),组合后是Endo(x => f(g(x))),即先执行g再执行f,对应你期望的3 + 2 + 1 = 6。- 移除重复的
cats.implicits.*导入(cats.syntax.all.*已包含其核心内容),可以减少隐式实例冲突的可能。
内容的提问来源于stack exchange,提问作者Max Smirnov
相关产品推荐
相关产品推荐

