这称为高阶抽象语法。
第一个解决方案:使用 Haskell 的 lambda。数据类型可能如下所示:
data Prop
= Not Prop
| And Prop Prop
| Or Prop Prop
| Impl Prop Prop
| Equiv Prop Prop
| Equals Obj Obj
| ForAll (Obj -> Prop)
| Exists (Obj -> Prop)
deriving (Eq, Ord)
data Obj
= Num Integer
| Add Obj Obj
| Mul Obj Obj
deriving (Eq, Ord)
你可以写一个公式为:
ForAll (\x -> Exists (\y -> Equals (Add x y) (Mul x y))))
这在The Monad Reader文章中有详细描述。强烈推荐。
第二种解决方案:
使用类似的字符串
data Prop
= Not Prop
| And Prop Prop
| Or Prop Prop
| Impl Prop Prop
| Equiv Prop Prop
| Equals Obj Obj
| ForAll String Prop
| Exists String Prop
deriving (Eq, Ord)
data Obj
= Num Integer
| Var String
| Add Obj Obj
| Mul Obj Obj
deriving (Eq, Ord)
然后你可以写一个公式
ForAll "x" (Exists "y" (Equals (Add (Var "x") (Var "y")))
(Mul (Var "x") (Var "y"))))))
优点是您可以轻松显示公式(很难显示Obj -> Prop
函数)。缺点是您必须在碰撞(~alpha 转换)和替换(~beta 转换)上编写更改名称。在这两种解决方案中,您都可以使用 GADT 而不是两种数据类型:
data FOL a where
True :: FOL Bool
False :: FOL Bool
Not :: FOL Bool -> FOL Bool
And :: FOL Bool -> FOL Bool -> FOL Bool
...
-- first solution
Exists :: (FOL Integer -> FOL Bool) -> FOL Bool
ForAll :: (FOL Integer -> FOL Bool) -> FOL Bool
-- second solution
Exists :: String -> FOL Bool -> FOL Bool
ForAll :: String -> FOL Bool -> FOL Bool
Var :: String -> FOL Integer
-- operations in the universe
Num :: Integer -> FOL Integer
Add :: FOL Integer -> FOL Integer -> FOL Integer
...
第三种解决方案:使用数字表示变量绑定的位置,较低的意思是更深。例如,在 ForAll (Exists (Equals (Num 0) (Num 1))) 中,第一个变量将绑定到 Exists,第二个变量将绑定到 ForAll。这被称为 de Bruijn 数字。看我不是一个数字 - 我是一个自由变量。