Haskell中的Mathmatic AST

Haskell中的Mathmatic AST,haskell,Haskell,我目前正在尝试用Haskell编写AST。更具体地说,我有一个将文本转换为AST的解析器,然后我希望能够将AST简化为另一个AST 例如x+x+x ->Add(Add(变量“x”)(变量“x”))(变量“x”) ->(Mul(Literal 3)(变量'x')) ->3x 我发现了其他的例子,但没有一个考虑到不同的数据类型。我想使用这种方法来允许根据二进制表达式左右两侧的内部类型来简化规则 以下是迄今为止我的数据类型的大致情况: data UnaryExpression o = Literal

我目前正在尝试用Haskell编写AST。更具体地说,我有一个将文本转换为AST的解析器,然后我希望能够将AST简化为另一个AST

例如
x+x+x

->
Add(Add(变量“x”)(变量“x”))(变量“x”)

->
(Mul(Literal 3)(变量'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...
我想我有两个问题: 首先,我需要有正确的数据结构,作为Haskell的新手,我不确定什么是正确的方法。
第二,我需要有一个simplify函数,它可以识别左数据类型和右数据类型。我觉得应该有办法做到这一点,但我不确定。

所以我认为你真正想要的是这样的:

  • AST o
    应该是一个数学表达式,表示数值类型的值
    o
  • 这可以是
    o
    类型的文本,也可以是二进制表达式,其中包含表示比
    o
    更专业的数字类型的表达式(例如
    Int
    Double
    更专业)
首先,始终保持简单并避免重复,因此对于所有二进制运算符,我们应该在
AST
中只有一个构造函数。为了区分不同的运算符,请创建一个单独的变体类型:

data NumOperator = Addition | Multiplication | Exponentiation
然后,你需要对你所说的“更专业的数字类型”有所了解。Haskell有一系列的数值类,但是没有标准的概念来说明哪些类型比其他类型更通用。实现这一点的一个库是,但它有点过于自由,“将任何内容转换为任何其他内容,而不管它在语义上是否清晰”。这里有一个简单的版本:

{-# 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
...
然后,需要一种在二进制运算符构造函数中存储不同类型的方法。这是存在量化,最好用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

所以我认为你真正想要的是这样的:

  • AST o
    应该是一个数学表达式,表示数值类型的值
    o
  • 这可以是
    o
    类型的文本,也可以是二进制表达式,其中包含表示比
    o
    更专业的数字类型的表达式(例如
    Int
    Double
    更专业)
首先,始终保持简单并避免重复,因此对于所有二进制运算符,我们应该在
AST
中只有一个构造函数。为了区分不同的运算符,请创建一个单独的变体类型:

data NumOperator = Addition | Multiplication | Exponentiation
然后,你需要对你所说的“更专业的数字类型”有所了解。Haskell有一系列的数值类,但是没有标准的概念来说明哪些类型比其他类型更通用。实现这一点的一个库是,但它有点过于自由,“将任何内容转换为任何其他内容,而不管它在语义上是否清晰”。这里有一个简单的版本:

{-# 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
...
然后,需要一种在二进制运算符构造函数中存储不同类型的方法。这是存在量化,最好用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

为什么不使用单一的表达式类型
data Expr=Literal Integer | Variable Char | Add Expr Expr | Mul Expr Expr |……
解析器如何可能生成这种类型的值?您是否计划为每个受支持的表达式结构使用不同的解析函数(这意味着您不支持任意嵌套)?还是希望它是多态的,然后作为类型参数提供结构?但这仍然需要您在解析字符串之前了解表达式的结构。@melpomene,因为您不能拥有具有不同内部类型的文本,正如我在原始帖子中所述。如果我使用这种结构,我将使用许多AST在线教程中列出的确切结构,我说我读过,但选择不读use@sepp2k. 目的是使每个二进制表达式都有一个left和right类型。例如,如果一个Add中嵌套了一个Add,我就需要一些适当的东西来表示“an
addinteger Double
可以简化为a
Double
。因此在任何时间点,每边只有两种类型。类似于
Add(Add Int Variable)Double
只需在每个位置跟踪两个内部类型level@ChaseWalden我在问你想要什么类型的
parse someString
应该是什么类型。按照我到目前为止对你的理解,你希望表达式
2*(3+4.0)
表示为
BinaryExpression-UnaryExpression-Integer(二进制表达式一元表达式整数一元表达式)将
42
作为
UnaryExpression Integer
。但是你也说过你有一个解析器,所以你不只是在代码中手动写下AST,你正在将任意字符串转换成AST。所以你有一个
解析
类型的函数
字符串->?
,我想知道
的位置是什么de>.data Expr=Literal Integer | Variable Char | Add Expr Expr | Mul Expr Expr |……
解析器如何生成这种类型的值?是否计划为每个受支持的表达式结构使用不同的解析函数(即不支持任意嵌套)?或者您希望它是多态的,然后将结构作为类型参数提供?但这仍然需要您在解析字符串之前知道表达式的结构。@melpomene,因为您不能像我在原始帖子中所说的那样拥有具有不同内部类型的文本。如果我使用该结构,我将使用G