{-# LANGUAGE FlexibleContexts, DeriveFoldable, TemplateHaskell,
TypeFamilies, DeriveFunctor, DeriveTraversable #-}
import Control.Applicative
import Data.Functor.Foldable
import Data.Functor.Foldable.TH
import Data.Maybe
我想使用“本地”重写器重写树:
data SKI = S | K | I | App SKI SKI deriving (Show)
makeBaseFunctor ''SKI
type Rewrite a = a -> Maybe a
match :: Rewrite SKI
match x = case x of
App I x -> Just x
_ -> Nothing
所以我编写了在树的不同点rewriteGlobal
应用相同的函数。match
它需要一个“本地”Rewrite t
并返回一个全局的。我只遍历树一次,没有尝试对每个树节点多次应用重写,直到不再可能进行重写。
但是有两种方法可以重写树。每次重写都对已重写的子树(WithChildren
模式)或“原始”子树(WithoutChildren
模式)进行操作。在后一种情况下,更高的重写“获胜”。
忽略 上的约束t
, 的类型rewriteGlobal
为:
data ChildRewrites = WithChildren | WithoutChildren
rewriteGlobal :: ChildRewrites -> Rewrite t -> Rewrite t
这是一个实现:
rewriteGlobal flag rewriteFn = para $ liftA2 (<|>)
(rewriteFn . handleChildren flag)
liftChildRewrites
handleChildren WithChildren = embed . fmap (uncurry fromMaybe)
handleChildren WithoutChildren = embed . fmap fst
liftChildRewrites cons | any (isJust . snd) cons = Just $ handleChildren WithChildren cons
liftChildRewrites _ = Nothing
liftChildRewrites
而且liftA2 (<|>)
看起来特别难看。
问题是:我不能做得更好吗?代码已经很短了,所以不是要让它更短。但是我是否朝着正确的方向前进,或者有比异态更好的局部匹配分布方法,例如更好的数据结构、单子方法或递归方案?
我没有避免替换的活页夹/名称/捕获,因此(P)HOAS 和类似方法可能不会更好。而且我没有实施 SKI 演算,这只是一个激励性的例子。
以下代码可用于测试:
test = App I $ App S $ App I $ K
bar = [rewrite WithChildren match test, rewrite WithoutChildren match test]
问题是:是否有为本地重写设计的特定结构/方法?如果这种专门的方法不存在并且通用方法是我能做的最好的,它只是基于意见的。