编辑:添加一个带有泛型注释的ProgramF
函数的示例。
是的,在toANF
的情况下,您使用它的方式是错误的。
在toANF
中,请注意您的Let bindingANF nbody
和bindingANF
及nbody
的伴侣定义只是特定构造函数Let
的fmap toANF
的重新实现。
也就是说,如果为您的ProgramF
派生了一个Functor
实例,则可以将toANF
片段重写为:
toANF :: LabelProgram -> Program
toANF (Fix (Ann label l@(Let _ _))) = Fix (fmap toANF l)
如果
toANF
只是去除标签,那么这个定义适用于所有构造函数,而不仅仅是
Let
,因此您可以省略模式匹配:
toANF :: LabelProgram -> Program
toANF (Fix (Ann label l)) = Fix (fmap toANF l)
根据@Regis_Kuckaertz的评论,现在你刚刚重新实现了forget
,其定义如下:
forget = Fix . fmap forget . unAnn . unFix
关于编写可在Program
,LabelProgram
等上通用的函数,我认为编写一个通用注释更有意义:
foo :: Attr ProgramF a -> Attr ProgramF a
如果你确实需要将它们应用于未注释的程序,请定义:
type ProgramU = Attr ProgramF ()
"U"在ProgramU
中代表"unit"。如果确实需要,你可以轻松地编写翻译器以将Program
作为ProgramU
处理:
toU :: Functor f => Mu f -> Attr f ()
toU = synthetise (const ())
fromU :: Functor f => Attr f () -> Mu f
fromU = forget
mapU :: (Functor f) => (Attr f () -> Attr f ()) -> Mu f -> Mu f
mapU f = fromU . f . toU
foo' :: Mu ProgramF -> Mu ProgramF
foo' = mapU foo
作为一个具体而愚蠢的例子,这里有一个函数,它将具有多个绑定的
Let
分开成嵌套的具有单一绑定的
Let
(并因此打破了在
Program
语言中的相互递归绑定)。它假设多重绑定
Let
上的注释将被复制到每个结果为单例
Let
:
splitBindings :: Attr ProgramF a -> Attr ProgramF a
splitBindings (Fix (Ann a (Let (x:y:xs) e)))
= Fix (Ann a (Let [x] (splitBindings (Fix (Ann a (Let (y:xs) e))))))
splitBindings (Fix e) = Fix (fmap splitBindings e)
它可以应用于一个示例 程序
:
testprog :: Program
testprog = Fix $ Unary (Fix $ Let [(Identifier "x", Fix $ Number 1),
(Identifier "y", Fix $ Number 2)]
(Fix $ Unary (Fix $ Number 3) NegOp))
NegOp
如下:
> mapU splitBindings testprog
Fix (Unary (Fix (Let {bindings = [(Identifier "x",Fix (Number 1))],
body = Fix (Let {bindings = [(Identifier "y",Fix (Number 2))],
body = Fix (Unary (Fix (Number 3)) NegOp)})})) NegOp)
>
这是我的完整工作示例:
{-# LANGUAGE DeriveFunctor #-}
{-# OPTIONS_GHC -Wall #-}
import Data.Generics.Fixplate
data Identifier = Identifier String deriving (Show)
data PLabel = PLabel deriving (Show)
data Operator = NegOp deriving (Show)
data ProgramF a
= Unary a
Operator
| Number Int
| Let { bindings :: [(Identifier, a)]
, body :: a }
deriving (Show, Functor)
instance ShowF ProgramF where showsPrecF = showsPrec
type Program = Mu ProgramF
type LabelProgram = Attr ProgramF PLabel
splitBindings :: Attr ProgramF a -> Attr ProgramF a
splitBindings (Fix (Ann a (Let (x:y:xs) e)))
= Fix (Ann a (Let [x] (splitBindings (Fix (Ann a (Let (y:xs) e))))))
splitBindings (Fix e) = Fix (fmap splitBindings e)
toU :: Functor f => Mu f -> Attr f ()
toU = synthetise (const ())
fromU :: Functor f => Attr f () -> Mu f
fromU = forget
mapU :: (Functor f) => (Attr f () -> Attr f ()) -> Mu f -> Mu f
mapU f = fromU . f . toU
testprog :: Program
testprog = Fix $ Unary (Fix $ Let [(Identifier "x", Fix $ Number 1),
(Identifier "y", Fix $ Number 2)]
(Fix $ Unary (Fix $ Number 3) NegOp))
NegOp
main :: IO ()
main = print $ mapU splitBindings testprog
Attr f a
只是Mu (Ann f a)
的同义词,因此您可以使用 fixplate 中提供的各种遍历方式。toANF
本质上是forget
,但根据您的意图,您不想从 AST 中删除标签,对吗?https://hackage.haskell.org/package/fixplate-0.1.7/docs/Data-Generics-Fixplate-Traversals.html - Regis Kuckaertz