为何表达式`foldr (mappend . Sum) 1 [2]`能通过类型检查?
为什么
foldr (mappend . Sum) 1 [2]能通过类型检查? 你手动β归约时的问题出在对初始值1的类型理解上——Haskell的整数字面量是多态的,它的类型不是固定的Integer,而是Num a => a。当上下文要求它是某个Num实例类型时,会自动通过Num类的fromInteger函数完成转换。
我们一步步拆解类型推导过程:
先明确
foldr的类型:foldr :: (a -> b -> b) -> b -> [a] -> b分析第一个参数
mappend . Sum的类型:Sum的类型是Sum :: a -> Sum amappend的类型是Monoid m => m -> m -> m- 组合后的
mappend . Sum类型为Num a => a -> Sum a -> Sum a(因为Sum的Monoid实例要求底层类型a属于Num,mappend对应加法操作)
结合第三个参数
[2](默认元素类型为Integer),可以推导出:foldr中的a是Integer- 函数
mappend . Sum的输出类型是Sum Integer,因此foldr的b类型必须是Sum Integer
初始值
1的类型被上下文约束为Sum Integer:
因为Sum a在a是Num时本身也是Num实例(Haskell为Sum自动派生了Num实例),所以整数字面量1会被自动转换为Sum (fromInteger 1),也就是Sum 1。
你手动归约时把foldr (mappend . Sum) 1 []直接写成了原始整数1,但实际上GHC处理时,这个初始值已经是Sum 1了。正确的归约流程应该是:
foldr (mappend . Sum) 1 [2] = (mappend . Sum) 2 (foldr (mappend . Sum) 1 []) = (mappend . Sum) 2 (Sum 1) -- 这里初始值是Sum 1,不是Integer 1 = mappend (Sum 2) (Sum 1) = Sum (2 + 1) = Sum 3
这样就完全符合mappend的类型要求(两个Sum Integer参数),自然不会有类型错误。
内容的提问来源于stack exchange,提问作者F. Zer
相关产品推荐
相关产品推荐

