函数提升泛化:如何实现多参数函数的批量lift操作?
解决多参数函数提升的简洁方案
这个问题我太懂了——手动写几十次<*>简直是噩梦!其实有个非常简洁的方法,完全不用硬写链式调用,正好符合你想要的magic (map ...)的形式。
核心思路
利用Haskell的Applicative类型类特性:我们可以把多参数函数先包装成pure值(放进Applicative上下文),然后用foldl自动把列表里的每个Applicative参数依次通过<*>应用上去,自动完成所有的函数提升操作。
具体实现
先定义一个magic函数,参数顺序先传你的参数列表,再传目标函数,完美匹配你想要的写法:
import Control.Applicative (pure, (<*>)) -- 接受Applicative参数列表和多参数函数,返回提升后的结果 magic :: Applicative f => [f a] -> (a -> a -> ... -> a -> b) -> f b magic args func = foldl (<*>) (pure func) args
然后你的代码就可以改成这样:
-- 把所有参数用map生成列表,传给magic即可,不用写一堆<*> k = magic (map someFunctionName [1,2,3,4,5,6,8,9,...,20]) myFunc
为什么这能行?
pure func把你的多参数函数(比如20元函数)包装成f (a -> a -> ... -> b)类型的Applicative值;foldl (<*>)会遍历参数列表,每次用<*>把当前的Applicative函数和下一个参数结合——每次<*>都会“消耗”一个函数参数,把函数的元数减一;- 遍历完所有参数后,就得到了最终的
f b类型结果,和你手动写一长串<*>的效果完全一致。
可选的参数顺序
如果你更习惯先写函数再传参数列表,也可以调整magic的参数顺序:
magic' :: Applicative f => (a -> a -> ... -> a -> b) -> [f a] -> f b magic' func args = foldl (<*>) (pure func) args -- 使用方式: k = magic' myFunc (map someFunctionName [1,2,...,20])
注意事项
- 这个方法要求所有参数的Applicative值类型一致(比如都是
Maybe Int或者IO String),因为Haskell的列表是同构的; - 函数的参数数量必须和列表的长度完全匹配,否则会出现类型错误(这其实是好事,能帮你提前发现参数数量不匹配的问题)。
内容的提问来源于stack exchange,提问作者radrow
相关产品推荐
相关产品推荐

