Haskell自定义ZipList'的Applicative实例异常问题问询
解决自定义ZipList'类型的Applicative实例问题
看起来你在给自定义的ZipList'类型实现Applicative实例时踩了几个常见的坑,我来帮你逐一理清问题和解决方案:
1. 长度≥3时<*>结果异常的原因与修复
你的问题核心是<*>的实现逻辑不符合ZipList的“对位应用”语义。标准ZipList的<*>是将函数列表和值列表对应位置的元素逐一配对应用,当其中一个列表耗尽时就停止。而你的实现可能错误地做了类似“笛卡尔积”的递归展开,导致多余的元素被生成。
错误实现的典型例子
假设你的<*>是这样写的(会产生多余元素):
data List a = Nil | Cons a (List a) deriving (Show) newtype ZipList' a = ZipList' (List a) deriving (Show) instance Applicative ZipList' where pure x = ZipList' (Cons x Nil) ZipList' fs <*> ZipList' xs = ZipList' (applyAll fs xs) where applyAll Nil _ = Nil applyAll _ Nil = Nil applyAll (Cons f fs') xs = Cons (f (headList xs)) (applyAll fs' xs ++ applyAll fs (tailList xs)) headList (Cons x _) = x tailList (Cons _ xs') = xs'
这种写法会把每个函数和后续所有值反复结合,自然会产生多余的元素。
正确的<*>实现
我们需要让函数和值对位遍历,同时消耗两个列表:
instance Applicative ZipList' where -- 标准ZipList的pure是无限重复的元素,这样才能和任意长度的列表配对 pure x = ZipList' (repeatList x) where repeatList x = Cons x (repeatList x) ZipList' fs <*> ZipList' xs = ZipList' (zipApply fs xs) where zipApply Nil _ = Nil zipApply _ Nil = Nil zipApply (Cons f fs') (Cons x xs') = Cons (f x) (zipApply fs' xs')
用这个实现测试你的例子:
> ZipList' (Cons (*2) (Cons (+99) (Cons (+4) Nil))) <*> ZipList' (Cons 5 (Cons 9 (Cons 1 Nil))) ZipList' (Cons 10 (Cons 108 (Cons 5 Nil)))
就会得到你预期的结果了。
2. <**>移出实例后编译报错的原因与解决
原因分析
<**>是Applicative类的默认方法,它的默认定义是:
(<**>) :: Applicative f => f a -> f (a -> b) -> f b x <**> f = f <*> x
在你的原实例中,因为你已经实现了<*>和pure,编译器会自动使用这个默认实现,它会正确处理ZipList'的包装类型。
如果你把<**>移出实例单独定义,很可能是没有正确处理ZipList'的包装,直接操作了内部的List类型,导致类型签名不匹配,编译器自然会报错说你传递了List而非ZipList'。
解决方法
如果你想单独定义<**>,必须确保函数的参数和返回值都是ZipList'类型,而不是内部的List:
-- 正确的单独定义方式 (<**>) :: ZipList' a -> ZipList' (a -> b) -> ZipList' b ZipList' xs <**> ZipList' fs = ZipList' (zipApply fs xs) where zipApply Nil _ = Nil zipApply _ Nil = Nil zipApply (Cons f fs') (Cons x xs') = Cons (f x) (zipApply fs' xs')
或者更简洁地复用已实现的<*>:
(<**>) x f = f <*> x
这样就能正常编译并工作了。
内容的提问来源于stack exchange,提问作者Aaron
相关产品推荐
相关产品推荐

