我是Haskell的新手,正在研究Haskell LLVM tutorial。在其中,作者定义了一个简单的代数数据类型来表示AST。
type Name = String
data Expr
= Float Double
| BinOp Op Expr Expr
| Var String
| Call Name [Expr]
| Function Name [Expr] Expr
| Extern Name [Expr]
deriving (Eq, Ord, Show)
data Op
= Plus
| Minus
| Times
| Divide
deriving (Eq, Ord, Show)
但是,这不是一个理想的结构,因为解析器实际上期望Expr
中的Extern
列表只包含表示变量的表达式(即在这种情况下的参数不能是任意表达式)。我想让类型反映这个约束(使用QuickCheck更容易生成随机有效的AST);但是,为了解析器函数(所有类型都有Parser Expr
)的一致性,我不只想说| Expr Name [Name]
。我想做这样的事情:
data Expr
= ...
| Var String
...
| Function Name [Expr] Expr
| Extern Name [Var] -- enforce constraint here
deriving (Eq, Ord, Show)
但是在Haskell中这是不可能的。
总而言之,Extern
和Var
都应该是Expr
,而Extern
应该有一个代表参数的Vars
列表。最好的方法是将所有这些分开并使它们成为Expr
类型类的实例(没有任何方法)?或者是否有更惯用的方法(或者更好地废弃这些类型并做一些完全不同的事情)?
答案 0 :(得分:5)
免责声明,我是您提到的LLVM教程的作者。
只需使用Extern Name [Name]
,教程第3章后面的所有内容都会使用exact definition。我想我只是忘了让第2章Syntax.hs
与其他人保持一致。
我不担心使解析器定义一致,它们可以返回不同的类型。这是后来解析器使用的内容。 identifier
只是来自LanguageDef的字母数字标识符的parsec,它成为AST中的Name
类型。
extern :: Parser Expr
extern = do
reserved "extern"
name <- identifier
args <- parens $ many identifier
return $ Extern name args