我有一个具有 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))'
如何为 编写 Functor 实例Expr2
?
我曾考虑将 Expr2 包装在一个 newtype 中,newtype Expr2 a = Expr2 (Fix (ExprF a))
但是这个 newtype 需要被解包才能传递给cata
,我不太喜欢。我也不知道是否可以Expr2
像我一样自动派生仿函数实例Expr1
。