我正在尝试为RoseTree数据类型编写Functor
实例。
data Rose a = MkRose a [Rose a]
deriving (Eq, Show)
instance Functor Rose where
fmap f (MkRose a children) = MkRose (f a) (fmap f children)
我真的认为这将工作,但我得到:
Expected type: [a]
Actual type: [Rose a]
对于孩子,谁能给我解释一下我错在哪里?
data Rose a = MkRose a [Rose a]
deriving (Eq, Show)
instance Functor Rose where
fmap f (MkRose a []) = MkRose (f a) []
fmap f (MkRose a roses) = MkRose (f a) ((fmap . fmap) f roses)
您面临的问题是由于Rose
的子节点被打包在一个额外的数据结构中,即列表[]
。你有一个递归的数据结构,每一步都有无限多个分支,比如递归路径。换句话说,它不像列表那样只有一条路径,也不像二叉树那样只有2或3条路径,这些路径的分支通常用特定的数据构造器命名,但每个节点可以有无限多个子节点。
因此,您需要通过使用fmap . fmap
构造向下钻取以访问您正在寻找的值。右边的fmap
(ghc为其提供了内置的Functor
)遍历列表中的每个元素,并将它们传递给左边的fmap
,后者钻取MkRose
的值,您已经为其编写了Functor
。
如果你看fmap
的签名,你有:
fmap :: Functor f => (a -> b) -> f a -> f b
将此应用于[]
得到:
fmap @[] :: (a -> b) -> [a] -> [b]
在我们的案例中,这是fmap . fmap
中右fmap
的签名。
fmap
应用于Rose
,得到签名:
fmap @Rose :: (a -> b) -> Rose a -> Rose b
这两个家伙的组合将右边的输出作为左边的输入,左边的与f
一起作用于它的输入,即玫瑰。
当f :: a -> b
,fmap f
对于任意一个函子F
具有类型F a -> F a
时。例如,我们可以使用
fmap f :: [a] -> [b]
fmap f :: Rose a -> Rose b -- in recursive calls
fmap f :: Maybe a -> Maybe b
fmap f :: IO a -> IO b
...
我想你明白我的意思了。然而,如果我们有两个函子,那么仅fmap f
是不够的。例如,我们没有
fmap f :: Maybe [a] -> Maybe [b]
是因为fmap
只适用于单个函子,而这里我们有两个函子(Maybe
和[]
)。如果需要使用嵌套的函函数,则需要fmap
:
f :: a -> b
fmap f :: [a] -> [b] -- add []
fmap (fmap f) :: Maybe [a] -> Maybe [b] -- add Maybe
在你的例子中,你需要[Rose a] -> [Rose b]
类型的东西,它涉及两个函子,所以你也必须两次使用fmap
。