我正在尝试创建一个简单的嵌入式DSL,它应该允许 递归定义。为此,我正在使用data-reify 将表达式的AST转换为图形。
在以下示例代码中,Signal
表示用户的AST
可以使用(参见test
信号)。它是静态类型的,所以它不是
例如,可以添加Signal Double
和Signal 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)
需要善良* -> *
。
答案 0 :(得分:1)
为什么不像使用其他地方那样使用GADT标记您的值,而不是使用类型类?
data Value v where
VFloat64 :: Double -> Value Double
VFloat32 :: Float -> Value Float
deriving instance Show (Value v)
在添加构造函数中使用Num t
现在毫无意义 - 您现在知道t
只能是Float
或Double
。
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)))