是否可以使用SYB来转换类型?

时间:2011-03-25 15:54:49

标签: haskell scrap-your-boilerplate

我想编写一个rename函数来替换String中的AST名称(代表层次标识符),其中GUID的名称(整数)来自于携带的符号表作为Renamer monad中的隐藏状态。

我有一个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)

以及从c Stringc 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一起使用。但是,这可能会导致AST在重命名后仍然可以保留StrName。我想使用类型系统正式捕获重命名的AST只能保留GUID名称的事实。

1 个答案:

答案 0 :(得分:2)

一个解决方案(可能效率低于您希望的)将使您的AST成为FunctorDataTypeable的实例(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版本要容易得多。