在Haskell实施Smullyan的算术鸟类

时间:2014-02-04 19:37:55

标签: haskell combinators combinatory-logic

在搜索Raymond Smullyan的 To Mock a Mockingbird 的信息时,我偶然发现Stephen Tetley's Haskell code for the basic combinators。我认为这是一个有趣的想法,并决定使用这些组合器从 To Mock a Mockingbird 的第24章实施“可以做算术的鸟”。我得到了定义真理,虚假,继承者,前任和零检查组合的功能,以及将组合布尔变为Haskell布尔值的功能,反之亦然。但是当我尝试创建一个带有组合数并返回整数的函数时,我会遇到问题。我想知道如何使这个功能工作。这是我到目前为止所得到的:

-- | The first 7 combinators below are from Stephen Tetley's Data.Aviary.Birds
-- | (http://hackage.haskell.org/package/data-aviary-0.2.3/docs/Data-Aviary-Birds.html),
-- | with minor modifications.


-- | S combinator - starling. 
-- Haskell: Applicative\'s @(\<*\>)@ on functions.
-- Not interdefined.
st :: (a -> b -> c) -> (a -> b) -> a -> c
st f g x = f x (g x)

-- | K combinator - kestrel - Haskell 'const'.
-- Corresponds to the encoding of @true@ in the lambda calculus.
-- Not interdefined.
ke :: a -> b -> a
ke a _b = a 

-- | I combinator - identity bird / idiot bird - Haskell 'id'.
id' :: a -> a 
id' = st ke ke 

-- | B combinator - bluebird - Haskell ('.').
bl :: (b -> c) -> (a -> b) -> a -> c
bl = st (ke st) ke

-- | C combinator - cardinal - Haskell 'flip'.
ca :: (a -> b -> c) -> b -> a -> c
ca = st(st(ke st)(st(ke ke)st))(ke ke)

-- | T combinator - thrush.
-- Haskell @(\#)@ in Peter Thiemann\'s Wash, reverse application.
th :: a -> (a -> b) -> b
th = ca id'

-- | V combinator - vireo.
vr :: a -> b -> (a -> b -> d) -> d
vr = bl ca th




-- | The code I added begins here. 




-- | Truth combinator. Functionally superfluous since it can  
-- | be replaced with the kestrel. Added for legibility only.
tr :: a -> b -> a
tr = ke

-- | Falsity combinator.
fs :: a -> b -> b
fs = ke id'

-- | Successor combinator.
sc :: a -> ((b -> c -> c) -> a -> d) -> d
sc = vr fs 

-- | Predecessor combinator.
pd :: ((((a -> a) -> b) -> b) -> c) -> c
pd = th (th id')

-- | Zerocheck combinator.
zc :: ((a -> b -> a) -> c) -> c
zc = th ke



-- | Below are 2 functions for changing combinatory booleans 
-- | to Haskell booleans and vice versa. They work fine as 
-- | far as I can tell, but I'm not very confident about 
-- | their type declarations. I just took the types 
-- | automatically inferred by Haskell.

-- | Combinatory boolean to Haskell boolean.
cbhb :: (Bool -> Bool -> Bool) -> Bool
cbhb cb  = cb True False

-- | Haskell boolean to combinatory boolean.
hbcb :: Bool -> a -> a -> a
hbcb hb | hb     = tr
        | not hb = fs




-- | Combinatory number to Haskell number. 
-- | This is the problematic function that I'd like to implement. 
-- | I think the function is logically correct, but I have no clue
-- | what its type would be. When I try to load it in Hugs it 
-- | gives me a "unification would give infinite type" error. 
cnhn cn | cbhb (zc cn) = 0
        | otherwise    = 1 + cnhn (pd cn)

这是我对代码错误的粗略猜测:'cnhn'函数将任何组合数作为参数。但是不同的组合数字有不同的类型,例如

零)id'​​:: a - &gt;一个

一)vr(ke id')id'::((a - &gt; b - &gt; b) - &gt;(c - &gt; c) - &gt; d) - &gt; d

二)vr(ke id')(vr(ke id')id')::((a - &gt; b - &gt; b) - &gt;(((c - &gt; d - &gt; d ) - &gt;(e - &gt; e) - &gt; f) - &gt; f) - &gt; g) - &gt;克

等等。因此,将任何组合数作为参数的函数必须能够采用无限多种类型的参数。但是Haskell不允许这样的功能。

为了简要说明,以下是我的3个主要问题:

1)'cnhn'功能有什么问题? (我的猜测是否正确?)

2)可以修复吗?

3)如果没有,我可以用什么其他语言来实现Smullyan的算术鸟类?

感谢您阅读一个很长的问题。

1 个答案:

答案 0 :(得分:2)

似乎后继组合器不太正确。这是Haskell中Church算法的编码,unchurch函数是cnhn(教会编号为Haskell编号,我推测?)试图实现的。

tr :: a -> b -> a
tr x y = x

fl :: a -> b -> b
fl x y = y

succ :: ((a -> b) -> c -> a) -> (a -> b) -> c -> b
succ n f x = f (n f x)

unbool :: (Bool -> Bool -> t) -> t
unbool n = n True False

unchurch :: ((Int -> Int) -> Int -> a) -> a
unchurch n = n (\i -> i + 1) 0

church :: Int -> (a -> a) -> a ->a
church n =
  if n == 0
  then zero
  else \f x -> f (church (n-1) f x)

zero :: a -> b -> b
zero = fl

one :: (a -> a) -> a -> a
one = succ zero

two :: (a -> a) -> a -> a
two = succ one

ten :: (a -> a) -> a -> a
ten = succ (succ (succ (succ (succ (succ (succ (succ (succ (succ zero)))))))))

main = do
  print (unchurch ten)
  print (unchurch (church 42))
  

所以一个以任何组合数作为参数的函数必须是   能够采取无限多种类型的论点。但是Haskell没有   允许这样的功能。

啊,但是因为Haskell允许参数多态性。如果你查看数字的编码,他们都有签名((a -> a) -> a -> a),所以参加教会号码的函数的参数只需要它的第一个参数统一它的第一个参数的类型变量与编码的函数教会号码。

例如,我们也可以定义一个乘法运算,它实际上只是一个高阶函数,它取两个教会数字并乘以它们。这适用于任何两个教会号码。

mult :: (a -> b) -> (c -> a) -> c -> b
mult m n f = m (n f)

test :: (a -> a) -> a -> a
test = mult ten ten

main = print (unchurch test) -- 100