Haskell 如何在将AST转换为图形(eDSL)时保留类型信息
我正在尝试创建一个简单的嵌入式DSL,它应该允许 递归定义。为此,我正在使用 将表达式的AST转换为图形 在下面的示例代码中,Haskell 如何在将AST转换为图形(eDSL)时保留类型信息,haskell,dsl,Haskell,Dsl,我正在尝试创建一个简单的嵌入式DSL,它应该允许 递归定义。为此,我正在使用 将表达式的AST转换为图形 在下面的示例代码中,信号表示用户的AST 可以使用(请参阅测试信号)。它是静态类型的,所以不是 例如,可以添加一个双信号和一个信号Int {-# LANGUAGE GADTs, TypeFamilies #-} import Control.Applicative hiding (Const) import Data.Reify data Value = VFloat64 Double
信号
表示用户的AST
可以使用(请参阅测试信号)。它是静态类型的,所以不是
例如,可以添加一个双信号
和一个信号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
{-#语言GADT,类型族}
导入控制。应用程序隐藏(Const)
导入数据。具体化
数据值=VFloat64双精度
|VFloat32浮子
派生(显示)
类hasa值,其中
值::a->value
实例HasValue Double,其中
值x=VFloat64 x
实例HasValue Float,其中
值x=VFloat32 x
数据信号t在哪里
添加::Num t=>Signal t->Signal t->Signal t
延迟::HasValue t=>t->Signal t->Signal t
常量::HasValue t=>t->Signal t
数据节点在哪里
nodead::s->s->Node s
节点显示::值->节点->节点
NodeConst::Value->Node s
派生(显示)
实例MuRef(信号t),其中
类型DeRef(信号t)=节点
mapDeRef(addab)=nodead f a b
mapDeRef f(常数x)=纯$NodeConst(值x)
mapDeRef f(延迟x a)=节点显示(值x)f a
测试:双信号
测试=添加(常数1.0)测试
为了评估DSL,首先将AST转换为节点
类型
使用data-reify
中的reifyGraph
。在目前的表述中,,
这涉及到使用HasValue
typeclass转换值
将每个信号转换为总和类型值
。不幸的是,这使得节点
图形
有效地动态键入,因为要计算nodead
,我将始终
必须检查两个参数是否使用相同的构造函数
所以我的问题是:是否有可能保留
在转换到节点时,信号
AST中仍然可用
图表
我已经尝试使用其他类型(即节点sa
)参数化节点
,
但这并没有奏效,因为DeRef(Signal t)
需要是*->*
类型,而不是使用类型类,为什么不像在其他地方那样使用GADTs为值标记类型
data Value v where
VFloat64 :: Double -> Value Double
VFloat32 :: Float -> Value Float
deriving instance Show (Value v)
在add构造函数中使用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
现在不需要值
,因为您的AST已经包含值
数据类型:
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
您是否可以在代码中包含求值部分(至少包括类型)?另外,您是否尝试过用Node a s
代替Node a
,这是否解决了Deref(Signal t)
kind的问题?这可能有帮助吗?谢谢,这个包裹看起来很有趣。不幸的是,文档有点稀疏…抱歉,没有回答。周末我无法登录。你的答案回答了这个问题,所以我接受。
let test = Add (Const (VFloat64 1.0)) test
> :t reifyGraph test
reifyGraph test :: IO (Graph (DeRef (Signal Double)))