将AST转换为Graph(eDSL)时如何保留类型信息

时间:2014-05-16 13:34:21

标签: haskell dsl

我正在尝试创建一个简单的嵌入式DSL,它应该允许 递归定义。为此,我正在使用data-reify 将表达式的AST转换为图形。

在以下示例代码中,Signal表示用户的AST 可以使用(参见test信号)。它是静态类型的,所以它不是 例如,可以添加Signal DoubleSignal Int

{-# LANGUAGE GADTs, TypeFamilies #-}
import Control.Applicative hiding (Const)
import Data.Reify

data Value = VFloat64 Double
           | VFloat32 Float 
  deriving (Show)

class HasValue a where
    value :: a -> Value

instance HasValue Double where
    value x = VFloat64 x

instance HasValue Float where
    value x = VFloat32 x

data Signal t where
    Add :: Num t => Signal t -> Signal t -> Signal t
    Delay :: HasValue t => t -> Signal t -> Signal t
    Const :: HasValue t => t -> Signal t

data Node s where
    NodeAdd :: s -> s -> Node s
    NodeDelay :: Value -> s -> Node s
    NodeConst :: Value -> Node s
  deriving (Show)

instance MuRef (Signal t) where
    type DeRef (Signal t) = Node
    mapDeRef f (Add a b) = NodeAdd <$> f a <*> f b
    mapDeRef f (Const x) = pure $ NodeConst (value x)
    mapDeRef f (Delay x a) = NodeDelay (value x) <$> f a

test :: Signal Double
test = Add (Const 1.0) test

要评估DSL,首先将AST转换为Node类型 使用reifyGraph中的data-reify。在目前的配方中, 这涉及使用HasValue类型类来转换值 每个信号的总和类型Value。不幸的是,这会产生Node图表 有效地动态输入,因为要评估NodeAdd,我将永远 必须检查两个参数是否使用相同的构造函数。

所以我的问题是:是否可以保留类型信息 在转换为Signal时,Node AST仍然可用 图表?

我已尝试使用其他类型(即Node)参数化Node s a, 但是这没有用,因为DeRef (Signal t)需要善良* -> *

1 个答案:

答案 0 :(得分:1)

为什么不像使用其他地方那样使用GADT标记您的值,而不是使用类型类?

data Value v where 
  VFloat64 :: Double -> Value Double
  VFloat32 :: Float  -> Value Float 

deriving instance Show (Value v) 

在添加构造函数中使用Num t现在毫无意义 - 您现在知道t只能是FloatDouble

data Signal t where
    Add :: Signal t -> Signal t -> Signal t
    Delay :: Value t -> Signal t -> Signal t
    Const :: Value t -> Signal t

现在,Node不会丢弃您的代码,而是保留它们:

data Node t s
    = NodeAdd s s 
    | NodeDelay (Value t) s 
    | NodeConst (Value t)
  deriving Show

现在不需要value,因为您的AST已包含Value数据类型:

instance MuRef (Signal t) where
    type DeRef (Signal t) = Node t
    mapDeRef f (Add a b) = NodeAdd <$> f a <*> f b
    mapDeRef _ (Const x) = pure $ NodeConst x
    mapDeRef f (Delay x a) = NodeDelay x <$> f a

在您的示例中,test的类型为(Fractional t, HasValue t) => Signal t。现在,t由构造函数修复,可以推断出正确的类型:

let test = Add (Const (VFloat64 1.0)) test

> :t reifyGraph test
  reifyGraph test :: IO (Graph (DeRef (Signal Double)))