Haskell中的数学AST

时间:2018-06-30 12:27:35

标签: haskell

我目前正在尝试在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还是陌生的,我不确定什么是正确的方法。 其次,我需要让我的简化函数了解左右数据类型。我觉得应该有办法做到这一点,但我不确定。

1 个答案:

答案 0 :(得分:1)

所以我认为您真正想要的是这样的东西:

  • AST o应该是表示数值类型为o的值的数学表达式。
  • 这可以是o类型的文字,也可以是包含比o(例如IntDouble更专业)。

首先,请始终保持简单并避免重复,因此对于所有二进制运算符,我们在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