Class 使[StackExp]成为Expr类的实例

Class 使[StackExp]成为Expr类的实例,class,haskell,instance,Class,Haskell,Instance,我为算术运算制作classExpr class Expr a where mul :: a -> a -> a add :: a -> a -> a lit :: Integer -> a 我想“解析”这样的东西:mul(add(lit3)(lit2))(lit4)=(3+2)*4 我有数据类型: data StackExp = PushI Integer | PushB Bool | Add

我为算术运算制作class
Expr

class Expr a where
  mul :: a -> a -> a 
  add :: a -> a -> a
  lit :: Integer -> a
我想“解析”这样的东西:mul(add(lit3)(lit2))(lit4)=(3+2)*4

我有数据类型:

data StackExp = PushI Integer
              | PushB Bool
              | Add
              | Mul
              | And
              | Or
                deriving Show

我的任务是:我需要为类型
Program

更具体地说,我想进行以下转换:
mul(add(lit 3)(lit 2))(lit 4)
->
[PushI 2,PushI 3,add,PushI 4,mul]

我遇到了问题,因为我在实例声明的输出中收到了
[[StackExp]]

我的尝试:

instance Expr Program where
  lit n = (PushI n):[]
  add exp1 exp2 = exp1:(exp2:[Add]) 
  mul exp1 exp2 = exp1:(exp2:[Mul])
我不知道如何将所有子表达式连接到列表中

----------------编译器错误如下所示------------------------

Couldn't match type `[StackExp]' with `StackExp'
    Expected type: StackExp
      Actual type: Program

因此,您基本上要做的是从表达式语言的抽象语法(type class
Expr
)编译为简单堆栈机器的代码(type
StackExpr
的元素列表)

然后您立即遇到的一个问题是,仅在Haskell 98或Haskell 2010中,您无法声明类的实例。例如,GHC将投诉

Illegal instance declaration for `Expr [StackExp]'
  (All instance types must be of the form (T a1 ... an)
   where a1 ... an are *distinct type variables*,
   and each type variable appears at most once in the instance head.
   Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Expr [StackExp]'
为了解决这个问题,您可以将
Program
定义为类型同构(而不是像现在这样仅仅是一个类型同义词):

然后使程序成为类Expr的实例。(或者,您可以根据上面的错误消息建议启用
FlexibleInstances
。)

现在我们可以编写所需的实例声明:

instance Expr Program where
  mul (P x) (P y) = P (x ++ y ++ [Mul])
  add (P x) (P y) = P (x ++ y ++ [Add])
  lit n           = P [PushI n]
也就是说,对于乘法和加法,我们首先编译操作数,然后分别生成
Mul
Add
指令;对于文本,我们生成相应的push指令

通过这一声明,我们可以得到,例如:

> mul (add (lit 3) (lit 2)) (lit 4) :: Program
P [PushI 3,PushI 2,Add,PushI 4,Mul]
(与您的示例不同。您将操作数的顺序交换为
Add
。由于加法是可交换的,因此我假设此版本也是可以接受的。)


当然,为堆栈程序编写一个小型计算器更有趣:

eval :: Program -> [Integer]
eval (P es) = go es []
  where
    go [] s                   = s
    go (PushI   n : es) s     = go es (n : s)
    go (Add : es) (m : n : s) = go es ((m + n) : s)
    go (Mul : es) (m : n : s) = go es ((m * n) : s)
(请注意,我忽略了这里处理布尔值的说明,因为您似乎只处理整数表达式。)

现在我们以你为例

> eval (mul (add (lit 3) (lit 2)) (lit 4))
[20]

这似乎是正确的。

对不起,我想我刚刚解决了问题:添加exp1 exp2=exp1++exp2++[add]mul exp1 exp2=exp1++exp2++[mul]如果有效,您可以回答自己的问题,这样这个问题就不会看起来还没有解决。谢谢您的回答。是的,我启用了灵活的实例,因为GHCi编译器建议我这样做=)。
eval :: Program -> [Integer]
eval (P es) = go es []
  where
    go [] s                   = s
    go (PushI   n : es) s     = go es (n : s)
    go (Add : es) (m : n : s) = go es ((m + n) : s)
    go (Mul : es) (m : n : s) = go es ((m * n) : s)
> eval (mul (add (lit 3) (lit 2)) (lit 4))
[20]