Изваждане на църковни цифри в haskell

Опитвам се да внедря църковни цифри в Haskell, но срещнах малък проблем. Haskell се оплаква от безкраен тип с

Възниква проверка: не може да се конструира безкраен тип: t = (t -> t1) -> (t1 -> t2) -> t2

когато се опитвам да правя изваждане. 99% съм сигурен, че моето ламбда смятане е валидно (въпреки че ако не е, моля, кажете ми). Това, което искам да знам, е дали има нещо, което мога да направя, за да накарам haskell да работи с моите функции.

module Church where

type (Church a) = ((a -> a) -> (a -> a))

makeChurch :: Int -> (Church a)
makeChurch 0 = \f -> \x -> x
makeChurch n = \f -> \x -> f (makeChurch (n-1) f x)

numChurch x = (x succ) 0

showChurch x = show $ numChurch x

succChurch = \n -> \f -> \x -> f (n f x)

multChurch = \f2 -> \x2 -> \f1 -> \x1 -> f2 (x2 f1) x1

powerChurch = \exp -> \n -> exp (multChurch n) (makeChurch 1)

predChurch = \n -> \f -> \x -> n (\g -> \h -> h (g f)) (\u -> x) (\u -> u)

subChurch = \m -> \n -> (n predChurch) m

person Probie    schedule 06.07.2011    source източник
comment
Трябва да направите декларацията на типа type Church a = (a -> a) -> a -> a. По-чист е, не е различен.   -  person alternative    schedule 06.07.2011
comment
Също така имайте предвид, че много помага да изпишете сигнатурите на типа. Ще ви каже точно къде е проблема...   -  person alternative    schedule 06.07.2011
comment
В крайна сметка премахнах сигнатурите на типа, за да видя дали ghci може да ги изведе правилно и да се надявам да се отърва от грешката (грешката не се е променила)... също така предпочитам скобите около типа. Това го кара да се откроява повече за мен   -  person Probie    schedule 06.07.2011


Отговори (3)


Проблемът е, че predChurch е твърде полиморфно, за да бъде правилно изведено чрез извод от типа Хиндли-Милнър. Например, изкушаващо е да напишем:

predChurch :: Church a -> Church a
predChurch = \n -> \f -> \x -> n (\g -> \h -> h (g f)) (\u -> x) (\u -> u)

но този тип не е правилен. Church a приема като свой първи аргумент a -> a, но вие предавате n функция с два аргумента, очевидно грешка в типа.

Проблемът е, че Church a не характеризира правилно цифра на Чърч. Чърчовото число просто представлява число - какво, за бога, може да означава този тип параметър? Например:

foo :: Church Int
foo f x = f x `mod` 42

Това проверява, но foo със сигурност не е число на Църквата. Трябва да ограничим типа. Църковните цифри трябва да работят за всяко a, а не само за конкретно a. Правилното определение е:

type Church = forall a. (a -> a) -> (a -> a)

Трябва да имате {-# LANGUAGE RankNTypes #-} в горната част на файла, за да разрешите типове като този.

Сега можем да дадем подписа на типа, който очакваме:

predChurch :: Church -> Church
-- same as before

Вие трябва да дадете сигнатура на типа тук, защото типовете от по-висок ранг не могат да бъдат изведени от Hindley-Milner.

Въпреки това, когато преминем към внедряване на subChurch възниква друг проблем:

Couldn't match expected type `Church'
       against inferred type `(a -> a) -> a -> a'

Не съм 100% сигурен защо се случва това, мисля, че forall се разгръща твърде свободно от програмата за проверка на типа. Това обаче не ме изненадва; типовете от по-висок ранг могат да бъдат малко крехки поради трудностите, които представляват за компилатора. Освен това, ние не трябва да използваме type за абстракция, ние трябва да използваме newtype (което ни дава повече гъвкавост в дефиницията, помага на компилатора при проверката на типа и маркира местата, където използваме изпълнение на абстракцията):

newtype Church = Church { unChurch :: forall a. (a -> a) -> (a -> a) }

И ние трябва да модифицираме predChurch, за да превъртаме и развиваме, както е необходимо:

predChurch = \n -> Church $ 
    \f -> \x -> unChurch n (\g -> \h -> h (g f)) (\u -> x) (\u -> u)

Същото с subChurch:

subChurch = \m -> \n -> unChurch n predChurch m

Но вече не се нуждаем от сигнатури на типове -- има достатъчно информация в преобръщането/развиването, за да изведем типовете отново.

Винаги препоръчвам newtypes, когато създавате нова абстракция. Редовните type синоними са доста редки в моя код.

person luqui    schedule 06.07.2011
comment
Що се отнася до грешката type, това се случва, защото в Haskell полиморфните типове трябва да бъдат инстанцирани само с аргументи на мономорфен тип: в type Church = forall a. (a -> a) -> (a -> a) тип променлива a трябва да е мономорфна, но в subChurch дефиниция това не е така (в (n predChurch) тип променлива a е настроена на Church, който е полиморфен). Ето подробно обяснение: okmij.org/ftp/Haskell/types.html#some -непредсказуемост - person Ed'ka; 06.07.2011

Тази дефиниция на predChurch не работи в просто въведено ламбда смятане, само в нетипизираната версия. Можете да намерите версия на predChurch, която работи в Haskell тук.

person Mikhail Glushenkov    schedule 06.07.2011
comment
Благодаря, това беше отговорът, който търсех. Просто се чудех дали има някаква магия, която мога да направя, за да накарам haskell да не се интересува от типа. Вече имам дефиниция, която работи в haskell, просто исках да знам дали мога да накарам нетипизираната версия да работи в haskell. Благодаря отново. - person Probie; 06.07.2011
comment
@Probie: Имайте предвид, че първият бит се отнася само до просто въведеното λ-изчисление, което е подобно на Haskell без нито едно от: полиморфни типове, класове типове, data и newtype и рекурсивни свързвания. - person C. A. McCann; 06.07.2011

Сблъсквал съм се със същия проблем. И го реших без добавяне на подпис на типа.

Ето решението с cons car, копирано от SICP.

cons x y = \m -> m x y
car z = z (\p q -> p)
cdr z = z (\p q -> q)

next z = cons (cdr z) (succ (cdr z))
pred n = car $ n next (cons undefined zero)

sub m n = n pred m

Можете да намерите пълния източник тук.

Наистина съм изумен, след като написах sub m n = n pred m и го заредих в ghci без грешка при типа!

Кодът на Haskell е толкова кратък! :-)

person wenlong    schedule 20.04.2012
comment
Това наистина не работи. Ако погледнете изведените типове в GHCi, те са твърде специализирани, така че напр. showChurch $ sub (plus three two) two дава типови грешки. - person hammar; 20.04.2012
comment
@hammar Ооооп, прав си. Тествах само sub two one. sub three two дава типови грешки. - person wenlong; 20.04.2012