为何两个相似Haskell函数定义中as模式的类型推断存在差异?
Why do
left and left' have different type signatures in Haskell? 这是个非常有意思的类型推断细节问题!核心原因就在于你在left'的第二个分支里用了as模式(r@(Right _)),这给Haskell的类型检查器加上了额外的约束,而left的实现没有这个限制。
先拆解left的类型推断逻辑
我们一步步看left的定义:
left f (Left x) = Left (f x) left _ (Right x) = Right x
- 第一个分支:输入是
Left x,我们暂时把x的类型记为t,所以输入的Either整体是Either t b;f x会生成一个新值,我们把它的类型记为a,所以f的类型是t -> a,返回的Left (f x)类型就是Either a b。 - 第二个分支:输入是
Right x(x的类型是b),直接返回Right x——这里的Right x类型是Either a b,其中a只需要和第一个分支返回的类型一致就行,完全不需要和输入的Either的Left部分类型(t)绑定。 - 综合两个分支,类型检查器就能推断出最通用的类型:
left :: (t -> a) -> Either t b -> Either a b,这个类型允许f把t类型转换成任意a类型,灵活性拉满。
再看left'的类型限制来源
现在看left'的定义:
left' f (Left x) = Left (f x) left' _ r@(Right _) = r
关键差异就在第二个分支的r@(Right _):你直接返回了输入的r,这意味着函数返回的Either类型必须和输入的Either类型完全一致。
- 第一个分支:输入的Either是
Either a b(x的类型是a),f x必须生成a类型的值——因为返回的Left (f x)要和第二个分支返回的r(类型Either a b)的Left部分类型匹配。 - 这就直接限制了
f的类型只能是a -> a(输入输出必须是同一种类型),不然Left (f x)的类型就会和r的类型冲突。 - 所以类型检查器只能推断出更严格的类型:
left' :: (a -> a) -> Either a b -> Either a b,这个类型把f限制成了同类型转换函数。
验证一下这个约束
如果强行给left'指定left的通用类型,编译器会直接报错:
-- 尝试给left'指定更通用的类型,编译失败 left' :: (t -> a) -> Either t b -> Either a b left' f (Left x) = Left (f x) left' _ r@(Right _) = r
错误信息会明确提示:Either t b和Either a b无法统一,因为你返回了输入的r(类型Either t b),但函数签名要求返回Either a b,只有当t=a时才能满足这个约束。
内容的提问来源于stack exchange,提问作者Z-Y.L
相关产品推荐
相关产品推荐

