什么是“废弃锅炉”?

时间:2015-01-30 20:32:59

标签: haskell functional-programming generic-programming scrap-your-boilerplate

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

1 个答案:

答案 0 :(得分:13)

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

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

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

doubleNegSimpl :: Exp -> Exp
doubleNegSimpl (Negate (Negate e)) = e
...

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

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


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

section :: Generic a => a -> AnnotatedTree

和"有效"注释树可以投射回某些品牌的ADT

retract :: Generic a => AnnotatedTree -> Maybe a

值得注意的是,我引入了Generic类型类来指出已定义sectionretract的类型。

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

everywhere' :: Generic a => (a -> a) -> (AnnotatedTree -> AnnotatedTree)

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

everywhere :: Generic a => (a -> a) -> (a -> a)
everywhere f a0 = fromJust . retract . everywhere' f . section

everywhere f a做什么?它尝试应用函数f"无处不在"在ADT a。换句话说,我们现在写下我们的双重否定简化如下

doubleNegSimpl :: Exp -> Exp
doubleNegSimpl (Negate (Negate e)) = e
doubleNegSimpl e                   = e

换句话说,只要redex id无法匹配,它就会充当(Negate (Negate _))。如果我们将everywhere应用于此

simplify :: Exp -> Exp
simplify = everywhere doubleNegSimpl

然后双重否定将被消除"无处不在"通过一般遍历。 ...样板已消失。