如何用Scala 3与Cats以<*>实现Haskell的Applicative函子函数
在Scala 3和Cats中用
<*>实现Haskell风格的Applicative函数组合 目标是实现Haskell代码(+) <$> (+1) <*> (+1) $10的等价效果,核心是利用函数作为Applicative Functor的特性,用Cats的<*>操作符完成组合,而非mapN。
原代码的问题分析
你的代码编译失败有两个核心原因:
- 错误使用
pure:对add1调用pure后,得到的是Int => (Int=>Int)(忽略输入、始终返回add1函数的函数),但<*>需要右侧是Int=>Int类型的函数。 - 缺少一次
<*>组合:仅执行一次<*>只能得到返回函数的中间结果,无法直接得到最终的数值输出。
修正后的实现方案
方案1:简洁版(用<$>和<*>组合)
利用Cats为函数类型提供的Applicative实例,直接用<$>(fmap的语法糖)和<*>完成组合:
import cats._ import cats.implicits._ val add1: Int => Int = _ + 1 val plus: (Int, Int) => Int = _ + _ // 等价于Haskell的(+) <$> (+1) <*> (+1) val finalFunc: Int => Int = (plus <$> add1) <*> add1 finalFunc(10) // 输出22
方案2:修正你的原代码
保留你定义的Func1类型,修正pure的使用并补充第二次<*>:
import cats._ import cats.implicits._ type Func1[Y] = Int => Y val add1: Func1[Int] = (x: Int) => x + 1 val addXY: Func1[Func1[Int]] = (x: Int) => (y: Int) => x + y // pure包装addXY,得到忽略输入、始终返回addXY的函数 val foo: Func1[Func1[Func1[Int]]] = Applicative[Func1].pure(addXY) // 直接使用add1,无需pure val bar: Func1[Int] = add1 // 第一次<*>:将foo的结果(Int=>Int)与bar结合,得到Int=>(Int=>Int) val foobar1: Func1[Func1[Int]] = foo <*> bar // 第二次<*>:将中间结果与add1结合,得到最终的Int=>Int函数 val foobar2: Func1[Int] = foobar1 <*> add1 foobar2(10) // 输出22
原理说明
对于函数类型Int => *(即Cats的Reader[Int, *]),Applicative的核心规则是:
pure(a):创建忽略输入、始终返回a的函数。<*>:将左侧函数的结果(A=>B)与右侧函数的结果(A)结合,用同一个输入值喂给两个函数后执行应用,即(f <*> g)(x) = f(x)(g(x))。
内容的提问来源于stack exchange,提问作者thlim
相关产品推荐
相关产品推荐

