求Haskell中(:)(.)函数的显式(pointful)形式及分步推导过程
(:)(.) 的显式形式与推导过程 嘿,我来帮你一步步拆解这个看起来有点诡异的Haskell函数!你第一步的推导是对的,咱们接着往下走~
1. 先明确两个基础函数的本质
要搞懂这个复合函数,得先把底层的两个核心函数摸透:
列表构造函数
(:):它的类型是a -> [a] -> [a],显式(pointful)定义是:(:) :: a -> [a] -> [a] (:) x xs = x : xs简单说,它接收一个元素
x和一个同类型元素的列表xs,返回把x放在xs最前面的新列表。而且它是柯里化的——如果只传第一个参数x,会返回一个等待第二个参数(列表)的函数:\xs -> x : xs。函数组合运算符
(.):类型是(b -> c) -> (a -> b) -> a -> c,显式定义是:(.) :: (b -> c) -> (a -> b) -> a -> c (.) f g x = f (g x)它的作用是把两个函数
f和g组合起来,返回一个先执行g再执行f的新函数。
2. 推导 (:)(.) 的等价形式
Haskell 中函数应用是左结合且优先级最高的,所以 (:)(.) 其实等价于 ((:) (.))——也就是把 (:) 当作普通函数,传入它的第一个参数 (.)。
因为 (:) 是柯里化的,当我们只给它传第一个参数 (.) 时,就会得到一个等待第二个参数(列表)的函数。代入 (:) 的显式定义:
((:) (.)) = \xs -> (.) : xs
这就是 (:)(.) 的显式(pointful)形式!如果给它起个更易懂的名字,就是:
prependCompositionOp :: [(b -> c) -> (a -> b) -> a -> c] -> [(b -> c) -> (a -> b) -> a -> c] prependCompositionOp xs = (.) : xs
3. 解读让人困惑的类型
你看到的类型 [(b->c)->(a->b)->a->c]->[(b->c)->(a->b)->a->c] 其实很好理解:
- 输入是一个列表,列表里的每个元素都必须和
(.)是同类型的函数(也就是能做函数组合的二元函数); - 输出是把标准的函数组合运算符
(.)加到这个列表最前面后的新列表,所以类型和输入完全一致。
举个实际例子:假设我们有两个自定义的函数组合变体:
-- 类似(.),但先打印日志再组合 logCompose :: (b -> c) -> (a -> b) -> a -> c logCompose f g x = print "Composing functions" >> f (g x) -- 反向组合(类似($)但组合) reverseCompose :: (a -> b) -> (b -> c) -> a -> c reverseCompose g f x = f (g x)
这两个函数的类型都和 (.) 兼容。那么:
(:)(.) [logCompose, reverseCompose] -- 等价于 (.) : [logCompose, reverseCompose] -- 结果是 [(.), logCompose, reverseCompose]
4. 总结一下
(:)(.) 本质上就是一个**专门给“函数组合类型的函数列表”前置标准组合运算符 (.)**的简单函数,它的显式形式就是接收一个列表 xs,返回 (.) : xs。你第一步把 (:)(.) 展开成 ((.) :) 完全正确,这其实就是它的无点(point-free)形式,再转成显式就是我们上面写的 \xs -> (.) : xs。
内容的提问来源于stack exchange,提问作者Niklas

