假设有一些数据类型来表达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)
如何正确表达?
答案 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表达式提供通用数据类型。请注意,您的类型已经有重要的重叠(Var
,App
),并且在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中的一个实现。