{-# 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 (<|>)
和test = App I $ App S $ App I $ K
bar = [rewrite WithChildren match test, rewrite WithoutChildren match test]
看起来特别难看。
问题是:我能做得更好吗?代码已经很短了,所以不是缩短代码。但是,我是朝着正确的方向前进,还是有更好的方法来分配局部匹配而不是一个多态性,例如:更好的数据结构,monadic方法还是递归方案?
我没有粘合剂/名称/捕获避免替换,因此(P)HOAS和类似的方法可能不会更好。而且我没有实施SKI演算,这只是一个激励人心的例子。
以下代码可用于测试:
Fragment
问题是:是否有为本地重写设计的特定结构/方法?如果不存在这种专门的方法,那么这只是基于意见的,而且我能做的最好的方法就是一般。