给定一个 Haskell 表达式,我想执行 alpha 转换,即。重命名一些非自由变量。
我已经开始为此实现我自己的函数,该函数在 haskell-src-exts Exp 树上工作,但结果令人惊讶地不平凡,所以我不禁想知道 - 是否有一个既定的易于使用的库这种源转换的解决方案?理想情况下,它应该与 haskell-src-exts 集成。
给定一个 Haskell 表达式,我想执行 alpha 转换,即。重命名一些非自由变量。
我已经开始为此实现我自己的函数,该函数在 haskell-src-exts Exp 树上工作,但结果令人惊讶地不平凡,所以我不禁想知道 - 是否有一个既定的易于使用的库这种源转换的解决方案?理想情况下,它应该与 haskell-src-exts 集成。
这是“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 转换的子表达式,然后对这些子表达式使用转换来修改您想要更改的名称。