在哈斯克尔解释Parigot的lambda-mu演算

Bob*_*Bob 8 continuations interpreter haskell lambda-calculus interpretation

人们可以解释Haskell中的lambda演算:

data Expr = Var String | Lam String Expr | App Expr Expr

data Value a = V a | F (Value a -> Value a)

interpret :: [(String, Value a)] -> Expr -> Value a
interpret env (Var x) = case lookup x env of
  Nothing -> error "undefined variable"
  Just v -> v
interpret env (Lam x e) = F (\v -> interpret ((x, v):env) e)
interpret env (App e1 e2) = case interpret env e1 of
  V _ -> error "not a function"
  F f -> f (interpret env e2)
Run Code Online (Sandbox Code Playgroud)

如何将上述解释器扩展到lambda-mu演算?我的猜测是它应该使用continuation来解释这个微积分中的其他结构.来自Bernardi&Moortgat论文的(15)和(16)是我期望的那种翻译.

因为Haskell是图灵完备的,所以可能会如何?

提示:有关mu binder的直观含义,请参阅本研究论文第197页上的注释.

Cac*_*tus 5

这是使用@ user2407038表示的文章中的减少规则的无意义音译(正如你所看到的,当我说无语时,我的意思是无意义):

{-# LANGUAGE DataKinds, KindSignatures, GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}

import Control.Monad.Writer
import Control.Applicative
import Data.Monoid

data TermType = Named | Unnamed

type Var = String
type MuVar = String

data Expr (n :: TermType) where
  Var :: Var -> Expr Unnamed
  Lam :: Var -> Expr Unnamed -> Expr Unnamed
  App :: Expr Unnamed -> Expr Unnamed -> Expr Unnamed
  Freeze :: MuVar -> Expr Unnamed -> Expr Named
  Mu :: MuVar -> Expr Named -> Expr Unnamed
deriving instance Show (Expr n)

substU :: Var -> Expr Unnamed -> Expr n -> Expr n
substU x e = go
  where
    go :: Expr n -> Expr n
    go (Var y) = if y == x then e else Var y
    go (Lam y e) = Lam y $ if y == x then e else go e
    go (App f e) = App (go f) (go e)
    go (Freeze alpha e) = Freeze alpha (go e)
    go (Mu alpha u) = Mu alpha (go u)

renameN :: MuVar -> MuVar -> Expr n -> Expr n
renameN beta alpha = go
  where
    go :: Expr n -> Expr n
    go (Var x) = Var x
    go (Lam x e) = Lam x (go e)
    go (App f e) = App (go f) (go e)
    go (Freeze gamma e) = Freeze (if gamma == beta then alpha else gamma) (go e)
    go (Mu gamma u) = Mu gamma $ if gamma == beta then u else go u

appN :: MuVar -> Expr Unnamed -> Expr n -> Expr n
appN beta v = go
  where
    go :: Expr n -> Expr n
    go (Var x) = Var x
    go (Lam x e) = Lam x (go e)
    go (App f e) = App (go f) (go e)
    go (Freeze alpha w) = Freeze alpha $ if alpha == beta then App (go w) v else go w
    go (Mu alpha u) = Mu alpha $ if alpha /= beta then go u else u

reduceTo :: a -> Writer Any a
reduceTo x = tell (Any True) >> return x

reduce0 :: Expr n -> Writer Any (Expr n)
reduce0 (App (Lam x u) v) = reduceTo $ substU x v u
reduce0 (App (Mu beta u) v) = reduceTo $ Mu beta $ appN beta v u
reduce0 (Freeze alpha (Mu beta u)) = reduceTo $ renameN beta alpha u
reduce0 e = return e

reduce1 :: Expr n -> Writer Any (Expr n)
reduce1 (Var x) = return $ Var x
reduce1 (Lam x e) = reduce0 =<< (Lam x <$> reduce1 e)
reduce1 (App f e) = reduce0 =<< (App <$> reduce1 f <*> reduce1 e)
reduce1 (Freeze alpha e) = reduce0 =<< (Freeze alpha <$> reduce1 e)
reduce1 (Mu alpha u) = reduce0 =<< (Mu alpha <$> reduce1 u)

reduce :: Expr n -> Expr n
reduce e = case runWriter (reduce1 e) of
    (e', Any changed) -> if changed then reduce e' else e
Run Code Online (Sandbox Code Playgroud)

它对于纸上的例子"有用":有

example 0 = App (App t (Var "x")) (Var "y")
  where
    t = Lam "x" $ Lam "y" $ Mu "delta" $ Freeze "phi" $ App (Var "x") (Var "y")   
example n = App (example (n-1)) (Var ("z_" ++ show n))
Run Code Online (Sandbox Code Playgroud)

我可以减少example n到预期的结果:

*Main> reduce (example 10)
Mu "delta" (Freeze "phi" (App (Var "x") (Var "y")))
Run Code Online (Sandbox Code Playgroud)

我把恐慌引用放在上面的"作品"上的原因是我对λμ演算没有直觉,所以我不知道它应该做什么.

  • 这是一件很棒的工作!但是,如果我理解得很好,你已经实现了lambda-mu微积分的操作语义.然而,我要求以lambda-mu演算转换为Haskell的形式提供指称语义.也就是说,正如我在我的问题中对lambda演算所做的那样:lambda-mu演算的lambda在Haskell中用lambda翻译,lambda-mu演算的应用在Haskell中应用. (4认同)
  • Erdi博士,这是一种非常雄辩的自称无知的方式. (3认同)
  • @Bob我认为这不是那么容易,因为lambda-mu更"强大",Haskell在基本概念中更差.你看,如果任务是将命令式程序转换为Haskell,那么如果不发明一些概念作为世界状态或IO monad并使命令式语句的语义具有"被提升"到的类型,则不能这样做.线程世界状态或"IO a".而这一发明可能是一件大事.lamda-mu演算的语义不是很自然地被翻译成带有`call/cc`的程序吗?那么让我们来寻找延续和语义...... (3认同)

use*_*038 3

注意:这只是部分答案,因为我不确定如何扩展解释器。

这似乎是 DataKinds 的一个很好的用例。数据类型Expr在命名或未命名的类型上建立索引。常规 lambda 构造仅生成命名项。

{-# LANGUAGE GADTs, DataKinds, KindSignatures #-} 

data TermType = Named | Unnamed 

type Var = String
type MuVar = String 

data Expr (n :: TermType) where 
  Var :: Var -> Expr Unnamed
  Lam :: Var -> Expr Unnamed -> Expr Unnamed
  App :: Expr Unnamed -> Expr Unnamed -> Expr Unnamed 
Run Code Online (Sandbox Code Playgroud)

附加的MuName结构可以操纵TermType.

  ...
  Name :: MuVar -> Expr Unnamed -> Expr Named 
  Mu :: MuVar -> Expr Named -> Expr Unnamed
Run Code Online (Sandbox Code Playgroud)

  • 我认为这个问题不需要“DataKinds”和“GADT”复杂性。我认为简单的事情就是向 `Expr` 添加一个 `Mu` 构造函数(现在都是未命名的术语),`data Expr = Var String | Lam String Expr | 林弦表达式 应用表达式 表达式 | Mu String Named `,以及未命名术语的另一种类型,`data Named = Named String Expr` 应该足够了。我还发现语义的描述难以理解。 (3认同)
  • 我不同意否决票。mu 表达式的数据类型是从问题中提出的内容向前迈出的坚实一步。 (2认同)