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

编写Pnt2d数据类型Functor实例时遭遇occurs check错误求助

问题与解决方案

问题描述

我定义了如下数据类型:

data Pnt2d i v = Pnt2d {_p2id:: i, _v2:: V2 v}

想为它实现Functor实例,让fmap作用在第二个组件(V2)的内部值上,写的代码是:

instance Functor (Pnt2d i) where 
fmap f (Pnt2d i v) = Pnt2d i (f v)

结果出现了"occurs check"错误。我知道Functor只能作用在多参数类型的第二个参数上,这里符合要求,但第二个类型参数是V2的内部参数,怀疑这是错误原因。试过用Lenses但没解决,希望得到有无Lenses的两种解决方案。错误信息如下:

src/Uniform/Point2d.hs:119:35: error:
    • Occurs check: cannot construct the infinite type: b ~ V2 b
    • In the second argument of ‘Pnt2d’, namely ‘(f v)’
      In the expression: Pnt2d i (f v)
      In an equation for ‘fmap’: fmap f (Pnt2d i v) = Pnt2d i (f v)
    • Relevant bindings include
        f :: a -> b (bound at src/Uniform/Point2d.hs:119:10)
        fmap :: (a -> b) -> Pnt2d i a -> Pnt2d i b
          (bound at src/Uniform/Point2d.hs:119:5)
    |
119 |     fmap f (Pnt2d i v) = Pnt2d i (f v)
    |                                   ^^^

src/Uniform/Point2d.hs:119:37: error:
    • Occurs check: cannot construct the infinite type: a ~ V2 a
    • In the first argument of ‘f’, namely ‘v’
      In the second argument of ‘Pnt2d’, namely ‘(f v)’
      In the expression: Pnt2d i (f v)
    • Relevant bindings include
        v :: V2 a (bound at src/Uniform/Point2d.hs:119:21)
        f :: a -> b (bound at src/Uniform/Point2d.hs:119:10)
        fmap :: (a -> b) -> Pnt2d i a -> Pnt2d i b
          (bound at src/Uniform/Point2d.hs:119:5)
    |
119 |     fmap f (Pnt2d i v) = Pnt2d i (f v)

错误原因

你写的代码里,v的类型是V2 a,但fmap要求f的类型是a -> b——直接把f作用在v上,相当于要求a等于V2 a,这是个无限递归的类型,Haskell自然会报occurs check错误。你的需求是让fmap映射到V2内部的v类型参数上,而不是映射V2本身,所以得调整fmap的实现。

无Lenses解决方案

因为V2本身已经是Functor实例(如果用的是linear库或自定义V2,只要它实现了Functor),可以直接复用它的fmap:

instance Functor (Pnt2d i) where
  fmap f (Pnt2d i v) = Pnt2d i (fmap f v)

如果是自定义的V2还没实现Functor,先给V2加个实例:

data V2 a = V2 a a deriving (Show)

instance Functor V2 where
  fmap f (V2 x y) = V2 (f x) (f y)

这样Pnt2d的fmap就能正确把函数应用到V2的两个元素上。

用Lenses的解决方案

如果想用Lens操作,先给_v2定义一个Lens(需要Control.Lens库):

import Control.Lens

_v2 :: Lens' (Pnt2d i v) (V2 v)
_v2 = lens _v2 (\p newV2 -> p { _v2 = newV2 })

然后Functor实例可以写成:

instance Functor (Pnt2d i) where
  fmap f = over (_v2 . mapped) f

这里_v2 . mapped是一个遍历器,能直接访问V2内部的元素,over函数会把f应用到这些元素上。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 06:18:19