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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:14:38