什么是"废弃锅炉"?

Ben*_*son 14 haskell functional-programming generic-programming scrap-your-boilerplate

我看到有人在讨论Scrap Your Boilerplate和Haskell中的泛型编程.这些术语是什么意思?我什么时候想要使用Scrap Your Boilerplate,我该如何使用它?

J. *_*son 15

通常在对复杂数据类型进行转换时,我们只需要影响结构的小部分 - 换句话说,我们只针对特定的可简化表达式,重新索引.

经典的例子是对一类整数表达式的双重否定消除:

data Exp = Plus Exp Exp | Mult Exp Exp | Negate Exp | Pure Int

doubleNegSimpl :: Exp -> Exp
doubleNegSimpl (Negate (Negate e)) = e
...
Run Code Online (Sandbox Code Playgroud)

即使在描述这个例子时,我也不愿写出整个...部分.它完全是机械的 - 仅仅是在整个过程中继续递归的引擎Exp.

这个"引擎"是我们打算废弃的样板.


为实现这一目标,Scrap Your Boilerplate建议了一种机制,通过该机制我们可以构建数据类型的"通用遍历".这些遍历完全正确地运行,而不知道有关所讨论的特定数据类型的任何内容.为此,非常粗略地说,我们有一个通用注释树的概念.它们比ADT大,所有ADT都可以投影到带注释的树的类型中:

section :: Generic a => a -> AnnotatedTree
Run Code Online (Sandbox Code Playgroud)

并且"有效"的注释树可以投射回某个品牌的ADT

retract :: Generic a => AnnotatedTree -> Maybe a
Run Code Online (Sandbox Code Playgroud)

值得注意的是,我正在引入Generic类型类来指示具有sectionretract定义的类型.

使用所有数据类型的这种通用的,带注释的树表示,我们可以一劳永逸地定义遍历.特别是,我们提供了一个接口(使用sectionretract策略性地),以便最终用户永远不会暴露给该AnnotatedTree类型.相反,它看起来有点像:

everywhere' :: Generic a => (a -> a) -> (AnnotatedTree -> AnnotatedTree)
Run Code Online (Sandbox Code Playgroud)

这样,结合最终和初始sectionretracts以及我们注释的树总是"有效"的不变量,我们有

everywhere :: Generic a => (a -> a) -> (a -> a)
everywhere f a0 = fromJust . retract . everywhere' f . section
Run Code Online (Sandbox Code Playgroud)

怎么everywhere f a办?它试图f在ADT中应用"无处不在" 的功能a.换句话说,我们现在写下我们的双重否定简化如下

doubleNegSimpl :: Exp -> Exp
doubleNegSimpl (Negate (Negate e)) = e
doubleNegSimpl e                   = e
Run Code Online (Sandbox Code Playgroud)

换句话说,id只要redex (Negate (Negate _))无法匹配,它就会起作用.如果我们适用everywhere于此

simplify :: Exp -> Exp
simplify = everywhere doubleNegSimpl
Run Code Online (Sandbox Code Playgroud)

然后通过一般遍历"双处"消除双重否定.该...样板已经一去不复返了.