4

给定一个 Haskell 表达式,我想执行 alpha 转换,即。重命名一些非自由变量。

我已经开始为此实现我自己的函数,该函数在 haskell-src-exts Exp 树上工作,但结果令人惊讶地不平凡,所以我不禁想知道 - 是否有一个既定的易于使用的库这种源转换的解决方案?理想情况下,它应该与 haskell-src-exts 集成。

4

1 回答 1

5

这是“Scrap Your Boilerplate”风格的泛型库大放异彩的问题之一!

我最熟悉的是package uniplate但我现在实际上并没有安装它,所以我将使用package中lens(非常相似的)功能。这里的想法是它使用Data.Data.Data(这是有史以来最好的限定名称)和相关类以多态方式执行通用操作。

这是最简单的示例:

alphaConvert :: Module -> Module
alphaConvert = template %~ changeName

changeName :: Name -> Name
changeName (Ident n) = Ident $ n ++ "_conv"
changeName n = n

(%~)运算符是 fromlens并且只是意味着将函数应用于changeName泛型 traversal 选择的所有内容template。所以它的作用是找到每个字母数字标识符并附_conv加到它。在它自己的源代码上运行这个程序会产生这样的结果:

module AlphaConv where
import Language.Haskell.Exts
import Control.Lens
import Control.Lens.Plated
import Data.Data.Lens

instance Plated_conv Module_conv
main_conv
  = do ParseOk_conv md_conv <- parseFile_conv "AlphaConv.hs"
       putStrLn_conv $ prettyPrint_conv md_conv
       let md'_conv = alphaConvert_conv md_conv
       putStrLn_conv $ prettyPrint_conv md'_conv

alphaConvert_conv :: Module_conv -> Module_conv
alphaConvert_conv = template_conv %~ changeName_conv

changeName_conv :: Name_conv -> Name_conv
changeName_conv (Ident_conv n_conv)
  = Ident_conv $ n_conv ++ "_conv"
changeName_conv n_conv = n_conv

不是非常有用,因为它不区分本地绑定的标识符和在外部范围内定义的标识符(例如被导入),但它展示了基本思想。

lens可能看起来有点吓人(它的功能远不止这些);您可能会发现uniplate或其他图书馆更平易近人。

您解决实际问题的方法是多部分转换,首先选择您想要在其中进行 alpha 转换的子表达式,然后对这些子表达式使用转换来修改您想要更改的名称。

于 2013-05-08T00:13:25.540 回答