从this Haskell Cafe post,并借用jyp的一些代码示例,我们可以在Haskell中构建一个简单的PHOAS求值器:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
import Data.Char
data Term v t where
Var :: v t -> Term v t
App :: Term v (a -> b) -> Term v a -> Term v b
Lam :: (v a -> Term v b) -> Term v (a -> b)
data Exp t = Exp (forall v. Term v t)
-- An evaluator
eval :: Exp t -> t
eval (Exp e) = evalP e
data Id a = Id {fromId :: a}
evalP :: Term Id t -> t
evalP (Var (Id a)) = a
evalP (App e1 e2) = evalP e1 $ evalP e2
evalP (Lam f) = \a -> evalP (f (Id a))
data K t a = K t
showTermGo :: Int -> Term (K Int) t -> String
showTermGo _ (Var (K i)) = "x" ++ show i
showTermGo d (App f x) = "(" ++ showTermGo d f ++ " " ++ showTermGo d x ++ ")"
showTermGo d (Lam a) = "@x" ++ show d ++ " " ++ showTermGo (d+1) (a (K d))
showTerm :: Exp t -> String
showTerm (Exp e) = showTermGo 0 e
字符串
这个实现允许我们创建,规范化和字符串化λ-演算术语。问题是,eval
的类型是Exp t -> t
而不是Exp t -> Exp t
。因此,我不清楚如何将术语评估为范式,然后将其字符串化。PHOAS可以吗?
1条答案
按热度按时间kyvafyod1#
让我们从最幼稚的事情开始:
字符串
因为我们需要一个函数
Term v a -> v a
,所以现在我们知道我们应该选择v
,这样它就包含了Term v
。我们可以选择v ~ Term v
,但是你不能直接使用递归类型,所以你需要创建一个新的数据类型:型
(我相信
FixTerm
类型与非参数HOAS类型同构。现在我们可以使用它来定义我们的评估函数:
型
这是可以的,但是不幸的是我们不能从中恢复原始的
Term v a
。这很容易看出,因为它从来没有产生Var
构造函数。我们可以再次尝试,看看我们在哪里卡住了:型
这一次我们需要一个函数
v a -> FixTerm a
。为了能够做到这一点,我们可以向FixTerm
数据类型添加一个case,这让人想起自由monad类型:型
现在我们可以定义顶级eval:
型