我目前正在尝试在Haskell中编写AST。更具体地说,我有一个解析器,可将文本转换为AST,然后希望能够将AST简化为另一个AST。
例如x + x + x
-> Add (Add (Variable 'x') (Variable 'x')) (Variable 'x')
-> (Mul (Literal 3) (Variable 'x'))
-> 3x
我发现了其他示例,但没有一个示例考虑到不同的数据类型。我想使用这种方法来允许简化规则,具体取决于二进制表达式左右两侧的内部类型。
到目前为止,我大致了解的是我的数据类型:
data UnaryExpression o = Literal o
| Variable Char
data BinaryExpression l lo r ro = Add (l lo) (r ro)
| Mul (l lo) (r ro)
| Exp (l lo) (r ro)
-- etc...
我认为我有2个问题: 首先,我需要具有正确的数据结构,并且对于Haskell还是陌生的,我不确定什么是正确的方法。 其次,我需要让我的简化函数了解左右数据类型。我觉得应该有办法做到这一点,但我不确定。
答案 0 :(得分:1)
所以我认为您真正想要的是这样的东西:
AST o
应该是表示数值类型为o
的值的数学表达式。o
类型的文字,也可以是包含比o
(例如Int
比Double
更专业)。首先,请始终保持简单并避免重复,因此对于所有二进制运算符,我们在AST
中只应有一个构造函数。为了区分不同的运算符,请创建单独的变量类型:
data NumOperator = Addition | Multiplication | Exponentiation
然后,您需要某种方式来理解“更专业的数字类型”的含义。 Haskell有许多数字类,但没有标准概念说明哪些类型比其他类型更通用。用于实现此目的的一个库是convertible,但它有点太宽松了,“不管语义上是否清楚如何,都可以将任何内容转换为其他任何内容”。这里是一个简单的版本:
{-# LANGUAGE MultiParamTypeClasses #-}
class ConvertNum a b where
convertNum :: a -> b
instance ConvertNum Int Int where convertNum = id
instance ConvertNum Double Double where convertNum = id
...
instance ConvertNum Int Double where convertNum = fromIntegral
...
然后,您需要一种在Binary-operator构造函数中存储不同类型的方法。这是现有量化,最好用GADT表示:
{-# LANGUAGE GADTs #-}
data AST o where
Literal :: o -> AST o
Variable :: String -> AST o
BinaryExpression :: (ConvertNum ol o, ConvertNum or o)
=> NumOperator -> AST ol -> AST or -> AST o