如何在GADT表达式中定义fmap?

时间:2014-12-29 16:06:24

标签: haskell functor gadt

给出一种简单的"语言":

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 ...

出于同样的原因,我无法对此进行宣传......

1 个答案:

答案 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