5

我想编写一个rename函数,用作为单子中隐藏状态的符号表中的名称(整数)替换我的String名称(代表分层标识符) 。ASTGUIDRenamer

我有一个AST a通过名称类型参数化的类型。AST 叶子中的名称类型为Name a

data Name a = Name a

这使得使用 SYB 变压器轻松定位它们。

解析器是键入的(为简洁起见忽略错误的可能性):

parse :: String -> AST String

我希望输入rename函数:

rename :: AST String -> Renamer (AST GUID)

是否可以使用 SYB 将所有Name String' 转换为Name GUID' 与转换器:

resolveName :: Name String -> Renamer (Name GUID)

以及所有其他值 from c Stringtoc GUID通过转换他们的孩子,并使用相同的构造函数将它们粘贴回一起,尽管使用不同的类型参数?

everywhereM功能接近我想要的,但它只能转换c a -> m (c a)而不能c a -> m (c b)

我的后备解决方案(除了手动编写样板)是从 中删除类型参数AST,并定义Name如下:

data Name = StrName String
          | GuidName GUID

以便输入重命名:

rename :: AST -> Renamer AST

使其与everywhereM. 但是,这会留下 an在重命名后AST仍然可以持有's的可能性。我想使用类型系统来正式捕获重命名只能包含名称StrName的事实。ASTGUID

4

1 回答 1

2

一种解决方案(可能比您希望的效率低)是使您的 AST 成为 , 的实例FunctorData并且Typeable(GHC 7 可能会为您派生所有这些)然后执行以下操作:

import Data.Generics.Uniplate.Data(universeBi) -- from the uniplate package
import qualified Data.Map as Map

rename :: AST String -> Renamer (AST GUID)
rename x = do
    let names = nub $ universeBi x :: [Name String]
    guids <- mapM resolveName names
    let mp = Map.fromList $ zip names guids
    return $ fmap (mp Map.!) x

两点:

  1. 我假设Name从 中消除该位很容易resolveName,但我怀疑它是。
  2. 您可以在 SYB 中切换universeBi为等效的东西,但我发现 Uniplate 版本更容易理解。
于 2011-06-18T12:40:36.223 回答