我目前正在尝试构建一个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。有什么方法可以让我更优雅,并且像第一个解决方案那样做吗?
答案 0 :(得分:3)
你的第一个解决方案只是一个没有意义的错误定义。 Variable
不是那里的类型,它是一个nullary值构造函数。您无法在类型定义中引用Variable
,因为您无法引用任何值,例如True
,False
或100
。
第二个解决方案实际上是我们可以在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