如何编写可组合函数?解析Haskell中函数组合的生效差异
如何确保Haskell函数具备组合兼容性?
要让函数能通过.操作符组合,核心要遵循函数组合的类型匹配规则,具体可以从这几点入手:
- 明确类型签名:先确认每个函数的输入输出类型,组合
g . f的前提是f的返回类型必须严格等于g的参数类型。比如g :: b -> c、f :: a -> b,组合后的类型就是a -> c。 - 利用柯里化调整参数:Haskell的多参数函数本质是柯里化的(返回函数的函数),可以通过部分应用参数,把多参数函数转换成符合组合要求的单参数函数。比如一个
a -> b -> c的函数,传入一个a参数后,就变成b -> c的函数,更容易和其他函数组合。 - 遵循“数据在后”的惯例:如果要频繁组合函数,尽量把核心处理的数据放在参数列表的最后。比如处理列表的函数,把
[Int]放在最后,这样部分应用前面的配置参数后,得到的函数就是[Int] -> ...,能直接和其他接受[Int]的函数组合。
为什么两个表达式一个能运行,一个不行?
先看给定的函数类型:
f1 :: Int -> [Int] -> Int -> [Int] f2 :: [Int] -> Int
Haskell中函数都是柯里化的,所以f1的类型可以拆解为Int -> ([Int] -> (Int -> [Int]))——每次传入一个参数,就返回一个接受下一个参数的函数。
表达式1:(f2 . f1 1 [1..10]) 1
f1 1 [1..10]是部分应用了f1的前两个参数,得到的函数类型是Int -> [Int](原本的f1需要3个参数,传完前两个后,剩下最后一个Int参数,返回[Int])。- 这时候
f2 . (f1 1 [1..10])完全符合组合规则:f2的输入类型是[Int],正好匹配f1 1 [1..10]的返回类型[Int]。组合后的函数类型是Int -> Int,传入参数1后自然能正常运行。
表达式2:(f2 . f1 1) [1..10] 1
f1 1是部分应用了第一个参数,得到的函数类型是[Int] -> Int -> [Int]——它接受一个[Int]参数后,返回的是Int -> [Int]的函数。- 现在尝试组合
f2 . f1 1:.操作符要求右边函数的返回类型必须等于左边函数的输入类型。但f1 1的返回类型是Int -> [Int],而f2的输入类型是[Int],两者完全不匹配,编译器会直接抛出类型错误,所以这个表达式无法运行。
内容的提问来源于stack exchange,提问作者Dudu Dudu
相关产品推荐
相关产品推荐

