Haskell多参数函数如何使用eta reduce进行简化?
多参数函数的Eta归约问题
问题背景
在完成fp-course的练习时,遇到了多参数函数无法直接用普通函数组合符.进行Eta归约的问题:
- 正确实现的
replicateA:
replicateA n ka = sequence $ replicate n ka
尝试写成replicateA = sequence . replicate时无法运行。
- 类似的
flatMap例子:
flatMap f xs = flatten $ map f xs
写成flatMap = flatten . map同样报错。
问题原因
普通的函数组合符.的类型是(b -> c) -> (a -> b) -> a -> c,它只能组合单参数函数。而replicate和map都是双参数的柯里化函数,直接用.组合时,参数的应用顺序和函数类型无法匹配:
replicate的类型是Int -> a -> [a],sequence的类型是Applicative f => [f a] -> f [a],sequence . replicate的类型会变成Int -> (a -> [f a]) -> f [a],和目标的Int -> f a -> f [a]不符。map的类型是(a -> b) -> [a] -> [b],flatten的类型是[[b]] -> [b],flatten . map的类型是(a -> b) -> ([a] -> [b]),但map f返回的是[[b]],直接组合时参数层级不匹配。
解决方法
针对多参数函数的Eta归约,需要使用嵌套的函数组合,通过.的嵌套来处理多参数的柯里化结构:
1. 处理replicateA
可以写成:
replicateA = (sequence .) . replicate
这里(sequence .)是将sequence包装成一个接受单参数函数的组合器,再和replicate组合,最终类型匹配Int -> f a -> f [a]。
2. 处理flatMap
同理,flatMap可以写成:
flatMap = (flatten .) . map
(flatten .)包装flatten后,和双参数的map组合,得到符合要求的(a -> [b]) -> [a] -> [b]类型。
另一种写法:自定义双参数组合符
你也可以自定义一个专门处理双参数函数的组合符,让写法更直观:
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d (.:) f g x y = f (g x y)
之后就可以这样写:
replicateA = sequence .: replicate flatMap = flatten .: map
内容的提问来源于stack exchange,提问作者zichao liu
相关产品推荐
相关产品推荐

