在Haskell实施Smullyan的算术鸟类

use*_*455 6 haskell combinators combinatory-logic

在搜索关于Raymond Smullyan的To Mock a Mockingbird的信息时,我偶然发现了Stephen Tetley的基本组合器的Haskell代码.我认为这是一个有趣的想法,并决定使用这些组合器从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)
Run Code Online (Sandbox Code Playgroud)

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

零)id'​​:: a - > a

一)vr(ke id')id'::((a - > b - > b) - >(c - > c) - > d) - > d

2)vr(ke id')(vr(ke id')id')::((a - > b - > b) - >(((c - > d - > d) - >(e - > e ) - > f) - > f) - > g) - > g

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

简而言之,这是我的三个主要问题:

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

2)可以修复吗?

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

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

Ste*_*ehl 2

似乎后继组合器不太正确。这是 Haskell 中 Church 算术的编码,该unchurch函数是(我猜是cnhnChurch Number 到 Haskell Number?)正在尝试实现的功能。

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))
Run Code Online (Sandbox Code Playgroud)

因此,采用任何组合数作为参数的函数必须能够采用无限多种类型的参数。但 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
Run Code Online (Sandbox Code Playgroud)