检查AST是否递归包含特定的构造函数

时间:2014-08-10 14:26:37

标签: haskell

采用这种简单的数据类型(来自Uniplate documentation):

data Expr = Val Int
          | Neg Expr
          | Add Expr Expr

我想检查表达式树是否包含特定操作(在我们的案例中为NegAdd)。

如果我们为Uniplate派生Expr,我们就可以使用universe来编写这两个简单的函数:

hasNeg :: Expr -> Bool
hasNeg e = not $ null [() | Neg{} <- universe e]

hasAdd :: Expr -> Bool
hasAdd e = not $ null [() | Add{} <- universe e]

我想提取公共代码并写一些&#34; generic&#34;函数将接受有关构造函数的一些信息,但我甚至无法想到匹配的类型签名,这通常是一个不好的标志。这个功能是否有意义,以及实现它的正确方法是什么?

谢谢!

1 个答案:

答案 0 :(得分:3)

Control.Lens.Plated具有与uniplate类似的API(性能也大致相同),但使用lens可以将prism用作第一类构造函数:

{-# LANGUAGE TemplateHaskell #-}

import Control.Applicative
import Control.Lens
import Control.Lens.Extras

data Expr = Val Int
          | Neg Expr
          | Add Expr Expr deriving (Eq, Show)

instance Plated Expr where
    plate f (Val i)   = pure (Val i)
    plate f (Neg e)   = Neg <$> f e
    plate f (Add a b) = Add <$> f a <*> f b 

makePrisms ''Expr -- derives _Val, _Neg and _Add prisms

hasNeg :: Expr -> Bool 
hasNeg = any (is _Neg) . universe

hasPrism :: Prism' Expr a -> Expr -> Bool
hasPrism p = any (is p) . universe

hasAdd :: Expr -> Bool
hasAdd = hasPrism _Add 

hasNegNeg :: Expr -> Bool
hasNegNeg = hasPrism (_Neg . _Neg) -- matches (Neg (Neg x))