如何解读Haskell函数组合的类型签名并正确传入参数
Haskell 组合函数
f1 . f2类型签名解读与调用规则 类型签名拆解
给定的组合函数签名如下:
f1 . f2 :: (Fractional (a -> a), Num a) => a -> (a -> a) -> a -> a
我们可以拆成两部分解读:
- 类型约束部分
(Fractional (a -> a), Num a):Num a表示类型变量a属于数字类型类,支持加减乘、数字字面量等数值操作Fractional (a -> a)是比较特殊的约束,说明当前作用域内为a -> a的函数类型自定义了Fractional类型类实例,允许把函数当做分数处理,支持除法、分数字面量等操作,常规Haskell环境默认没有这个实例。
- 参数与返回值部分
a -> (a -> a) -> a -> a:
Haskell的函数默认柯里化,这个签名说明组合后的f1 . f2总共接收3个参数,最终返回a类型的值:- 第一个参数为
Num a约束下的a类型数值 - 第二个参数为
a -> a类型的函数 - 第三个参数为
a类型数值 - 最终返回值为
a类型数值
- 第一个参数为
调用合法性推导
我们首先回忆函数组合子(.)的定义:(f . g) x = f (g x),即组合后的函数会先把输入传给右侧的g,再把g的输出传给左侧的f,结合上面的类型可以推导出两个函数的大致类型边界:
f2接收a类型的数值作为输入,输出a -> a类型的函数(满足Fractional约束)f1接收f2输出的a -> a函数作为输入,最终输出柯里化的参数链a -> (a -> a) -> a -> a
对应你给出的两个调用示例,合法性解释如下:
示例1:(f1 . f2 2) 3 4
- 首先执行
f2 2:给f2传入第一个a类型参数2,返回a -> a类型的函数,作为组合子的右操作数 - 组合后的
f1 . (f2 2)只需接收剩余的两个a类型参数3、4即可得到结果,完全符合柯里化传参规则
示例2:(f1 2 . f2 2) 4
你写的f1 2.应为笔误,实际写法是(f1 2) . (f2 2):
- 首先执行
f2 2返回a -> a类型的函数 - 再执行
f1 2:提前给f1传入第一个a类型参数2,得到类型为(a -> a) -> a -> a的偏应用函数,刚好可以接收f2 2的输出作为输入 - 最终组合后的函数只需要传入最后一个
a类型参数4就可以得到返回值,符合类型约束要求
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

