我正在尝试将简单的eDSL“编译”为Atom语言。这里出现的问题是,我的类型/函数的类型类约束与Atom语言的类型约束不匹配。
编译为Atom的一个eDSL是copilot,它也有同样的问题,以相当冗长的方式解决它。以下是涉及的数据类型的简化版本:
{-# LANGUAGE GADTs #-}
data Type a where
TFloat :: Type Float
data Op1 a b where
Neg :: Type a -> Op1 a a
class NumE a where
instance NumE Float
data Exp e where
ENeg :: NumE a => Exp a -> Exp a
Type
和Op1
是新eDSL的一部分,NumE
和Exp
属于编译目标。要在某些时刻在eDSL之间进行转换,我需要一个具有以下类型的函数op2exp
:
op2exp :: Op1 a b -> Exp a -> Exp b
现在,Atom处理此问题的方式是rather verbose:
data NumEInst e = NumE e => NumEInst
numEInst :: Type a -> NumEInst a
numEInst TFloat = NumEInst
op2exp :: Op1 a b -> Exp a -> Exp b
op2exp op = case op of
Neg t -> case numEInst t of NumEInst -> ENeg
这很有效,但是非常繁琐且充满重复。
问题:
有没有办法,使用新的语言功能,以更简单的方式编写op2exp
函数?理想情况下是:
op2exp (Neg t) = ENeg
理想情况下,我甚至不需要Type
数据类型,并让编译器确定所有类型都匹配。
答案 0 :(得分:1)
您可以使用类型Op1
的类型参数,使目标语言中的* -> Constraint
数据类型参数化。看看Atom和Ivory库,我认为这样的事情应该有效:
{-# LANGUAGE GADTs, ConstraintKinds #-}
data Op1 expr a b where
Neg :: (expr a, Num a) => Op1 expr a a
class AtomExpr a where
instance AtomExpr Float
data AtomExp e where
ENeg :: (AtomExpr a, Num a) => AtomExp a -> AtomExp a
op2exp :: Op1 AtomExpr a b -> AtomExp a -> AtomExp b
op2exp Neg = ENeg