我有一个递归数据类型,它具有Functor实例:
data Expr1 a
= Val1 a
| Add1 (Expr1 a) (Expr1 a)
deriving (Eq, Show, Functor)
现在,我有兴趣修改这种数据类型以支持一般的递归方案,正如这个教程和这个Hackage包中所描述的那样。我设法使卡特曼分解起作用:
newtype Fix f = Fix {unFix :: f (Fix f)}
data ExprF a r
= Val a
| Add r r
deriving (Eq, Show, Functor)
type Expr2 a = Fix (ExprF a)
cata :: Functor f => (f a -> a) -> Fix f -> a
cata f = f . fmap (cata f) . unFix
eval :: Expr2 Int -> Int
eval = cata $ \case
Val n -> n
Add x y -> x + y
main :: IO ()
main =
print $ eval
(Fix (Add (Fix (Val 1)) (Fix (Val 2))))
但是现在我无法弄清如何给Expr2
相同的函子实例,就像原来的Expr
一样。当尝试定义函子实例时,似乎存在类型不匹配的问题。
instance Functor (Fix (ExprF a)) where
fmap = undefined
Kind mis-match
The first argument of `Functor' should have kind `* -> *',
but `Fix (ExprF a)' has kind `*'
In the instance declaration for `Functor (Fix (ExprF a))'
如何为Expr2
编写一个Functor实例?
我考虑过使用newtype Expr2 a = Expr2 (Fix (ExprF a))
将Expr2包装起来,但是这个新类型需要被解包才能被传递给cata
,这让我不太喜欢。我也不知道是否可以像我对Expr1
所做的那样自动派生出Expr2
的functor实例。
Expr2
的新类型,是否有一种方法可以像我对Expr1
所做的那样自动派生函子实例? - hugomgnewtype Expr a = Expr (Fix (ExprF a))
,因为Fix
不是一个函数子类型。如果你将其改为data Fix f a = Fix (f (Fix f a))
,那么你就可以编写一个类似于instance Functor f => Functor (Fix f)
的函数子类型实例,并且你不需要使用新类型。 - user2407038