从lambda项转换为组合项

时间:2012-10-21 19:49:02

标签: haskell lambda-calculus equivalence k-combinator s-combinator

假设有一些数据类型来表达lambda和组合术语:

data Lam α = Var α                   -- v
           | Abs α (Lam α)           -- λv . e1
           | App (Lam α) (Lam α)     -- e1 e2
             deriving (Eq, Show)

infixl 0 :@
data SKI α = V α                     -- x
           | SKI α :@ SKI α          -- e1 e2
           | I                       -- I
           | K                       -- K
           | S                       -- S
             deriving (Eq, Show)

还有一个函数来获取lambda术语的自由变量列表:

fv ∷ Eq α ⇒ Lam α → [α]
fv (Var v) = [v]
fv (Abs x e) = filter (/= x) $ fv e
fv (App e1 e2) = fv e1 ++ fv e2

要将lambda术语转换为组合术语,抽象消除规则可能很有用:

convert ∷ Eq α ⇒ Lam α → SKI α

1)T [x] => X

convert (Var x) = V x

2)T [(E 1 E 2)] => (T [E 1] T [E 2])

convert (App e1 e2) = (convert e1) :@ (convert e2)

3)T [λx.E] => (K T [E])(如果x在E中不是自由出现的)

convert (Abs x e) | x `notElem` fv e = K :@ (convert e)

4)T [λx.x] =>我

convert (Abs x (Var y)) = if x == y then I else K :@ V y

5)T [λx.λy.E] => T [λx.T[λy.E]](如果x在E中自由出现)

convert (Abs x (Abs y e)) | x `elem` fv e = convert (Abs x (convert (Abs y e)))

6)T [λx。(E 1 E 2)] => (S T [λx.E1] T [λx.E₂])

convert (Abs x (App y z)) = S :@ (convert (Abs x y)) :@ (convert (Abs x z))

convert  _ = error ":["

由于5)

,此定义无效
Couldn't match expected type `Lam α' with actual type `SKI α'
In the return type of a call of `convert'
In the second argument of `Abs', namely `(convert (Abs y e))'
In the first argument of `convert', namely
  `(Abs x (convert (Abs y e)))'

所以,我现在拥有的是:

> convert $ Abs "x" $ Abs "y" $ App (Var "y") (Var "x")
*** Exception: :[

我想要的是(希望我算得正确):

> convert $ Abs "x" $ Abs "y" $ App (Var "y") (Var "x")
S :@ (S (KS) (S (KK) I)) (S (KK) I)

问题

如果lambda术语和组合术语有不同类型的表达式,5)如何正确表达?

4 个答案:

答案 0 :(得分:1)

让我们考虑方程T [λx.λy.E] => T [λx.T[λy.E]。

我们知道T [λy.E]的结果是SKI表达式。由于它是由案例3,4或6中的一个产生的,因此它是我或应用程序(:@)。

因此,T [λx.T[λy.E]]中的外部T必须是案例3或6中的一个。您可以在代码中执行此案例分析。对不起,我没有时间写出来。

答案 1 :(得分:1)

这里最好为组合器和lambda表达式提供通用数据类型。请注意,您的类型已经有重要的重叠(VarApp),并且在lambda表达式中使用组合器并没有什么坏处。

我们想要消除的唯一可能性是在组合词中使用lambda抽象。我们可以使用索引类型禁止它们。

在下面的代码中,术语的类型由该术语中嵌套的lambda抽象的数量来参数化。 convert函数返回Term Z a,其中Z表示零,因此返回的术语中没有lambda抽象。

有关单身人士类型的更多信息(在此处稍作使用),请参阅论文Dependently Typed Programming with Singletons

{-# LANGUAGE DataKinds, KindSignatures, TypeFamilies, GADTs, TypeOperators,
    ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances #-}

data Nat = Z | Inc Nat

data SNat :: Nat -> * where
  SZ :: SNat Z
  SInc :: NatSingleton n => SNat n -> SNat (Inc n)

class NatSingleton (a :: Nat) where
  sing :: SNat a

instance NatSingleton Z where sing = SZ
instance NatSingleton a => NatSingleton (Inc a) where sing = SInc sing

type family Max (a :: Nat) (b :: Nat) :: Nat
type instance Max Z a = a
type instance Max a Z = a
type instance Max (Inc a) (Inc b) = Inc (Max a b)

data Term (l :: Nat) a where
  Var :: a -> Term Z a
  Abs :: NatSingleton l => a -> Term l a -> Term (Inc l) a
  App :: (NatSingleton l1, NatSingleton l2)
      => Term l1 a -> Term l2 a -> Term (Max l1 l2) a
  I   :: Term Z a
  K   :: Term Z a
  S   :: Term Z a

fv :: Eq a => Term l a -> [a]
fv (Var v) = [v]
fv (Abs x e) = filter (/= x) $ fv e
fv (App e1 e2) = fv e1 ++ fv e2
fv _ = []

eliminateLambda :: (Eq a, NatSingleton l) => Term (Inc l) a -> Term l a
eliminateLambda t =
  case t of
    Abs x t ->
      case t of
        Var y
          | y == x -> I
          | otherwise -> App K (Var y)
        Abs {} -> Abs x $ eliminateLambda t
        App a b -> S `App` (eliminateLambda $ Abs x a)
                     `App` (eliminateLambda $ Abs x b)
    App a b -> eliminateLambdaApp a b

eliminateLambdaApp
  :: forall a l1 l2 l . 
     (Eq a, Max l1 l2 ~ Inc l,
      NatSingleton l1,
      NatSingleton l2)
  => Term l1 a -> Term l2 a -> Term l a
eliminateLambdaApp a b =
  case (sing :: SNat l1, sing :: SNat l2) of
    (SInc _, SZ    ) -> App (eliminateLambda a) b
    (SZ    , SInc _) -> App a (eliminateLambda b)
    (SInc _, SInc _) -> App (eliminateLambda a) (eliminateLambda b)

convert :: forall a l . Eq a => NatSingleton l => Term l a -> Term Z a
convert t =
  case sing :: SNat l of
    SZ -> t
    SInc _ -> convert $ eliminateLambda t

答案 2 :(得分:1)

关键的洞察力是S,K和I只是恒定的Lam术语,就像1,2和3是恒定的Int一样。通过与'convert'函数相反来进行规则5类型检查非常容易:

nvert :: SKI a -> Lam a
nvert S = Abs "x" (Abs "y" (Abs "z" (App (App (Var "x") (Var "z")) (App (Var "y") (Var "z")))))
nvert K = Abs "x" (Abs "y" (Var "x"))
nvert I = Abs "x" (Var "x")
nvert (V x) = Var x
nvert (x :@ y) = App (nvert x) (nvert y)

现在我们可以使用'nvert'进行规则5类型检查:

convert (Abs x (Abs y e)) | x `elem` fv e = convert (Abs x (nvert (convert (Abs y e))))

我们可以看到左边和右边是相同的(我们将忽略守卫),除了左边的'Abs y e'被右边的'nvert(convert(Abs y e))'替换。由于'convert'和'nvert'是彼此相反的,我们总是用'nvert(转换x​​)'替换任何Lam'x',同样我们总是可以用任何SKI替换任何SKI'x' 'convert(nvert x)',所以这是一个有效的等式。

不幸的是,虽然它是一个有效的等式,但它不是一个有用的函数定义,因为它不会导致计算进展:我们只会永远地来回转换'Abs y e'!

要打破这个循环,我们可以用'提醒'替换对'nvert'的调用,我们应该稍后再做。我们通过向Lam添加新的构造函数来实现这一点:

data Lam a = Var a                   -- v
           | Abs a (Lam a)           -- \v . e1
           | App (Lam a) (Lam a)     -- e1 e2
           | Com (SKI a)             -- Reminder to COMe back later and nvert
             deriving (Eq, Show)

现在规则5使用此提醒而不是'nvert':

convert (Abs x (Abs y e)) | x `elem` fv e = convert (Abs x (Com (convert (Abs y e))))

现在我们需要通过制定一个单独的规则来实现我们的回复承诺,通过实际调用'nvert'替换提醒,如下所示:

convert (Com c) = convert (nvert c)

现在我们终于可以打破循环:我们知道'convert(nvert c)'总是与'c'相同,所以我们可以用这个替换上面的行:

convert (Com c) = c

请注意,我们对'convert'的最终定义实际上并没有使用'nvert'!它仍然是一个方便的功能,因为涉及Lam的其他功能可以使用它来处理新的'Com'案例。

你可能已经注意到我实际上已经将这个构造函数命名为'Com',因为它只是一个被包裹起来的COMbinator,但我认为采用稍长的路线比仅仅说“结束你的SKI”更有用。在Lams“:)

如果您想知道为什么我将该功能称为“nvert”,请参阅http://unapologetic.wordpress.com/2007/05/31/duality-terminology/:)

答案 3 :(得分:0)

Warbo是对的,组合子是常数lambda项,因此转换函数是
T[ ]:L -> C,其中L是一组lambda项,C是组合项和C⊂L。

因此规则T[λx.λy.E] => T[λx.T[λy.E]]

没有输入问题

Here Scala中的一个实现。