Haskell AST具有递归类型

时间:2016-11-13 09:53:23

标签: haskell lambda-calculus

我目前正在尝试构建一个lambda演算求解器,我在构建AST时遇到了一些问题。 lambda演算术语归纳为:

1)变量

2)lambda,变量,点和lambda表达式。

3)括号,lambda表达式,lambda表达式和括号。

我想做的事(最初尝试过)是:

data Expr = 
    Variable
  | Abstract Variable Expr
  | Application Expr Expr

现在显然这不起作用,因为Variable不是一个类型,而Abstract Variable Expr需要类型。所以我对此的hacky解决方案是:

type Variable = String

data Expr = 
    Atomic Variable
  | Abstract Variable Expr
  | Application Expr Expr

现在这真的很烦人,因为我自己不喜欢原子变量,但是抽象采用字符串而不是expr。有什么方法可以让我更优雅,并且像第一个解决方案那样做吗?

2 个答案:

答案 0 :(得分:3)

你的第一个解决方案只是一个没有意义的错误定义。 Variable不是那里的类型,它是一个nullary值构造函数。您无法在类型定义中引用Variable,因为您无法引用任何值,例如TrueFalse100

第二个解决方案实际上是我们可以在BNF中写的东西的直接翻译:

var  ::= <string>
term ::= λ <var>. <term> | <term> <term> | <var>

因此没有任何问题。

答案 1 :(得分:1)

你真正想要的是拥有类似

的类型
data Expr 
  = Atomic Variable
  | Abstract Expr Expr
  | Application Expr Expr

但是将Expr构造函数中的第一个Abstract限制为仅Atomic。在Haskell中没有直接的方法来执行此操作,因为某种类型的值可以由此类型的任何构造函数创建。因此,唯一的方法是为现有类型(如Variable类型别名)创建一些单独的数据类型或类型别名,并将所有常用逻辑移入其中。 Variable的解决方案似乎对我很好。

但是。您可以使用Haskell中的一些其他高级功能以不同的方式实现目标。您可以使用glambda包启发,该包使用GADT来创建类型化的lambda演算。另请参阅此答案:https://stackoverflow.com/a/39931015/2900502

我可以提出下一个解决方案来实现最小目标(如果你只想限制Abstract的第一个参数):

{-# LANGUAGE GADTs          #-}
{-# LANGUAGE KindSignatures #-}

data AtomicT
data AbstractT
data ApplicationT

data Expr :: * -> * where
    Atomic      :: String -> Expr AtomicT
    Abstract    :: Expr AtomicT -> Expr a -> Expr AbstractT
    Application :: Expr a -> Expr b -> Expr ApplicationT

下一个例子工作正常:

ex1 :: Expr AbstractT
ex1 = Abstract (Atomic "x") (Atomic "x")

但由于类型不匹配,此示例无法编译:

ex2 :: Expr AbstractT
ex2 = Abstract ex1 ex1