You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Church编码数字列表求和函数类型不通过的解决方案问询

问题

基于丘奇编码自然数和右折叠表示的列表的标准定义,我尝试编写一个接收数字列表并返回元素和的函数:

type Number = forall a. (a -> a) -> a -> a

type List a = forall b. (a -> b -> b) -> b -> b

zero :: Number
zero _ x = x

plus :: Number -> Number -> Number
plus a b f x = a f (b f x)

sum :: List Number -> Number
sum xs = xs plus zero

但这个sum函数无法通过类型检查,原因是它的类型展开后为:

(forall a. (a -> a) -> a -> a) -> (forall a. (a -> a) -> a -> a) -> (forall a. (a -> a) -> a -> a)

而函数定义实际需要的类型是:

forall a. ((a -> a) -> a -> a) -> ((a -> a) -> a -> a) -> (a -> a) -> a -> a

调整类型注解后的版本可以通过检查:

sum :: List ((a -> a) -> a -> a) -> (a -> a) -> a -> a  -- OK
sum xs = xs plus' zero
  where
    plus' :: (t1 -> t -> t3) -> (t1 -> t2 -> t) -> t1 -> t2 -> t3  -- 编译器推导的类型
    plus' a b f x = a f (b f x)
    zero _ x = x

我希望避免在所有定义中重复书写冗长的(a -> a) -> a -> a类型,除了定义type Number a = (a -> a) -> a -> a之外,有没有更巧妙的实现方法?

解决方案

方法一:用newtype包装多态函数(最推荐)

用newtype把丘奇数字的多态函数包起来,既能保留原有的语义,又能让类型系统顺利通过检查,还不用重复写冗长的类型:

{-# LANGUAGE RankNTypes #-}

newtype Number = Number { runNumber :: forall a. (a->a)->a->a }

type List a = forall b. (a -> b -> b) -> b -> b

zero :: Number
zero = Number $ \_ x -> x

plus :: Number -> Number -> Number
plus (Number a) (Number b) = Number $ \f x -> a f (b f x)

sum :: List Number -> Number
sum xs = xs plus zero

这里Number变成了一个具体的新类型,折叠列表时plus的类型Number -> Number -> Number完全符合List Number的折叠函数要求,类型检查直接通过。需要使用数字的具体功能时,调用runNumber就能拿到底层的多态函数。

方法二:调整plus的多态性适配折叠需求

不用包装类型,而是修改plus的类型签名,让它能接受针对特定类型的丘奇数字,而非封闭的多态数字:

{-# LANGUAGE RankNTypes #-}

type Number = forall a. (a->a)->a->a
type List a = forall b. (a -> b -> b) -> b -> b

zero :: Number
zero _ x = x

-- 让plus适配任意特定类型a的数字运算
plus :: ((a->a)->a->a) -> ((a->a)->a->a) -> (a->a)->a->a
plus a b f x = a f (b f x)

sum :: List Number -> Number
sum xs = xs (\n acc -> plus n acc) zero

这里把plus改成了针对特定a的多态函数,传给列表折叠时,会自动实例化到合适的类型,避免了原版本中多态类型无法统一的问题。

方法三:用类型族统一命名

如果不想定义带参数的type,可以用类型族来统一指代(a->a)->a->a:

{-# LANGUAGE RankNTypes, TypeFamilies #-}

type family NumberF a where
  NumberF a = (a->a)->a->a

type Number = forall a. NumberF a

type List a = forall b. (a -> b -> b) -> b -> b

zero :: Number
zero _ x = x

plus :: NumberF a -> NumberF a -> NumberF a
plus a b f x = a f (b f x)

sum :: List Number -> Number
sum xs = xs (\n acc -> plus n acc) zero

这种方式和定义type Number a = ...类似,但用类型族可以更灵活地扩展类型关联逻辑。

内容的提问来源于stack exchange,提问作者shooqie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 17:33:32