给出一种简单的"语言":
data Expr a where
ConstE :: a -> Expr a
FMapE :: (b -> a) -> Expr b -> Expr a
instance Functor Expr where
fmap = FMapE
interpret :: Expr a -> a
interpret (ConstE a) = a
interpret (FMapE f a) = f (interpret a)
由此我想提取一个调用图,例如:
foo = fmap show . fmap (*2) $ ConstE 1
应该产生图Node 1 -> Node (*2) -> Node show
。理想情况下,我希望将其存储在Data.Graph
。
我现在提到的是,应该可以使用System.Mem.StableNames
来识别单个节点并将其存储在HashMap (StableName (Expr a)) (Expr a)
中。
toHashMap :: Expr a -> HashMap (StableName (Expr a)) (Expr a)
toHashMap n@ConstE = do
sn <- makeStableName n
return $ HashMap.singleton sn n
问题是,似乎没有办法通过FMapE
节点:
toHashMap n@(FMapE _ a) = do
snN <- makeStableName n
snA <- makeStableName a
-- recurse
hmA <- toHashMap a
-- combine
return $ HashMap.singleton snN n `HashMap.union` hmA
GHC将抱怨这一点:
Couldn't match type ‘t’ with ‘b’
because type variable ‘b’ would escape its scope
This (rigid, skolem) type variable is bound by
a pattern with constructor
FMapE :: forall a b. (b -> a) -> Expr b -> Expr a,
in an equation for ‘toHashMap’
我可以看到这不会匹配...但我不清楚如何使这项工作。
这可能归结为编写children
函数:
children :: Event a -> [Event a]
children (ConstE) = []
children (FMapE _ a) = [a] -- doesn't match ...
出于同样的原因,我无法对此进行宣传......
答案 0 :(得分:1)
您可以从Uniplate1
class I've described previously获得一种类型为* -> *
的树的视频排序遍历,这是一种托付类型。
{-# LANGUAGE RankNTypes #-}
import Control.Applicative
import Control.Monad.Identity
class Uniplate1 f where
uniplate1 :: Applicative m => f a -> (forall b. f b -> m (f b)) -> m (f a)
descend1 :: (forall b. f b -> f b) -> f a -> f a
descend1 f x = runIdentity $ descendM1 (pure . f) x
descendM1 :: Applicative m => (forall b. f b -> m (f b)) -> f a -> m (f a)
descendM1 f a = uniplate1 a f
transform1 :: Uniplate1 f => (forall b. f b -> f b) -> f a -> f a
transform1 f = f . descend1 (transform1 f)
transform1
是一种通用的后序变换。 Uniplate1
的通用后序Monadic遍历是
transformM1 :: (Uniplate1 f, Applicative m, Monad m) =>
(forall b. f b -> m (f b)) ->
f a -> m (f a)
transformM1 f = (>>= f) . descendM1 (transformM1 f)
我们可以为Uniplate1
编写Expr
个实例:
instance Uniplate1 Expr where
uniplate1 e p = case e of
FMapE f a -> FMapE f <$> p a
e -> pure e
为了演示目的,我们会制作一个简单的dump
函数,并在单一效果后制作bypass
来恢复数据。
dump :: Expr b -> IO ()
dump (ConstE _) = putStrLn "ConstE"
dump (FMapE _ _) = putStrLn "FMapE"
bypass :: Monad m => (a -> m ()) -> a -> m a
bypass f x = f x >> return x
我们可以按拓扑顺序遍历您的示例
> transformM1 (bypass dump) (fmap show . fmap (*2) $ ConstE 1)
ConstE
FMapE
FMapE