Tip kontrolü ve özyinelemeli türler (Haskell / Ocaml'da Y birleştiricisinin yazılması)


21

Y birleştiricisini Haskell bağlamında açıklarken, genellikle ileriye dönük uygulamanın özyinelemeli türü nedeniyle Haskell'de yazım denetimi yapmayacağı genellikle not edilir.

Örneğin, Rosettacode'dan :

The obvious definition of the Y combinator in Haskell canot be used
because it contains an infinite recursive type (a = a -> b). Defining
a data type (Mu) allows this recursion to be broken.

newtype Mu a = Roll { unroll :: Mu a -> a }

fix :: (a -> a) -> a
fix = \f -> (\x -> f (unroll x x)) $ Roll (\x -> f (unroll x x))

Ve gerçekten de, “açık” tanım onay yazmıyor:

λ> let fix f g = (\x -> \a -> f (x x) a) (\x -> \a -> f (x x) a) g

<interactive>:10:33:
    Occurs check: cannot construct the infinite type:
      t2 = t2 -> t0 -> t1
    Expected type: t2 -> t0 -> t1
      Actual type: (t2 -> t0 -> t1) -> t0 -> t1
    In the first argument of `x', namely `x'
    In the first argument of `f', namely `(x x)'
    In the expression: f (x x) a

<interactive>:10:57:
    Occurs check: cannot construct the infinite type:
      t2 = t2 -> t0 -> t1
    In the first argument of `x', namely `x'
    In the first argument of `f', namely `(x x)'
    In the expression: f (x x) a
(0.01 secs, 1033328 bytes)

Aynı sınırlama Ocaml'da da var:

utop # let fix f g = (fun x a -> f (x x) a) (fun x a -> f (x x) a) g;;
Error: This expression has type 'a -> 'b but an expression was expected of type 'a                                    
       The type variable 'a occurs inside 'a -> 'b

Bununla birlikte, Ocaml'da, -rectypesanahtardan geçerek özyinelemeli türlere izin verilebilir :

   -rectypes
          Allow  arbitrary  recursive  types  during type-checking.  By default, only recursive
          types where the recursion goes through an object type are supported.

Kullanarak -rectypes, her şey çalışıyor:

utop # let fix f g = (fun x a -> f (x x) a) (fun x a -> f (x x) a) g;;
val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun>
utop # let fact_improver partial n = if n = 0 then 1 else n*partial (n-1);;
val fact_improver : (int -> int) -> int -> int = <fun>
utop # (fix fact_improver) 5;;
- : int = 120

Yazma sistemleri ve yazım çıkarımı hakkında meraklı olduğum için, hala cevaplayamadığım bazı soruları gündeme getiriyor.

  • Öncelikle, tür denetleyicisi türden nasıl gelir t2 = t2 -> t0 -> t1? Bu tip ile karşılaştıktan sonra, problemin ( t2) type ( ) 'ın kendisinin sağ tarafa atıfta bulunması olduğunu tahmin ediyorum.
  • İkincisi ve belki de en ilginç olanı, Haskell / Ocaml tipi sistemlerin buna izin vermemesinin nedeni nedir? Orada tahmin olduğunu Ocaml de bile varsayılan olarak buna izin vermez çünkü iyi bir neden olabilir verilirse özyinelemeli tipleri ile anlaşma -rectypesanahtarı.

Bunlar gerçekten büyük konularsa, ilgili literatürdeki işaretçileri takdir ediyorum.

Yanıtlar:


16

İlk olarak, GHC hatası,

GHC x, ilk önce bir işlev olarak kullandığımız birkaç kısıtlamayı birleştirmeye çalışıyor.

x :: a -> b

Sonra onu bu işleve değer olarak kullanırız

x :: a

Ve nihayet bunu orijinal argüman ifadesiyle birleştirelim

x :: (a -> b) -> c -> d

Şimdi x xbirleştirmek için bir girişim olur t2 -> t1 -> t0o birleştirici gerektirecektir beri Ancak, biz bu birleştirmek edemez, t2birinci, argüman xile, x. Dolayısıyla bizim hata mesajımız.

Sonra, neden genel özyinelemeli türleri değil. Dikkat edilmesi gereken ilk nokta equi ve iso özyinelemeli türleri arasındaki farktır.

  • eş-özyinelemeli, beklediğiniz mu X . Typeşey, keyfi olarak genişletmeye veya katlamaya tamamen eşdeğerdir.
  • izo-özyinelemeli türler, bir dizi işleç sağlar foldve unfoldbunlar özyinelemeli tür tanımlarını katlayan ve açan.

Şimdi eşit-özyinelemeli türler ideal ses, ancak karmaşık tip sistemlerde doğru elde etmek çok zor. Aslında tip kontrolü kontrol edilemez yapabilir. OCaml'ın tip sisteminin her ayrıntısına aşina değilim, ancak Haskell'deki tamamen zorlayıcı türler typechecker'ın türleri birleştirmeye çalışırken keyfi bir şekilde dönmesine neden olabilir, varsayılan olarak Haskell, tür kontrolünün sona erdiğinden emin olur. Dahası, Haskell'de, eş anlamlı kelimeler aptaldır, en faydalı özyinelemeli türler gibi tanımlanır type T = T -> (), ancak hemen hemen Haskell'de belirtilir, ancak özyinelemeli bir türün satırını içine koyamazsınız, sonsuzdur! Bu nedenle, Haskell'deki özyinelemeli türler eş anlamlıların nasıl ele alındığına dair büyük bir revizyona ihtiyaç duyacak, muhtemelen bir dil uzantısı olarak koyma çabasına değmeyecektir.

İzo-özyinelemeli türler kullanmak biraz acı vericidir, az ya da çok, tip denetleyicisine türlerinizi nasıl katlayacağınızı ve açacağınızı, programlarınızı okumak ve yazmak için daha karmaşık hale getirmek için açıkça söylemeniz gerekir.

Ancak bu, türünüzle yaptığınız işe çok benziyor Mu. Rollkatlanmış ve unrollaçılmış durumda. Aslında, içimizde pişirilmiş izo-özyinelemeli tiplerimiz var. Ancak, eşit-özyinelemeli tipler çok karmaşık, bu yüzden OCaml ve Haskell gibi sistemler sizi tekrar tekrar tip tip sabit noktalardan geçmeye zorluyorlar.

Şimdi, eğer bu sizi ilgilendirirse, Türleri ve Programlama Dillerini öneririm. Kopyalarım kucağımda oturuyor ve doğru terminolojiye sahip olduğumdan emin olmak için yazıyorum :)


Özellikle bölüm 21, indüksiyon,
coinduction

Teşekkür ederim! Bu gerçekten büyüleyici. Şu anda TAPL okuyorum ve bunun daha sonra kitapta ele alınacağını duyduğuma sevindim.
beta

@beta Yep, TAPL ve ağabeyi, Tipler ve Programlama Dillerinde İleri Konular harika kaynaklar.
Daniel Gratzer 29:13

2

OCaml'da -rectypesderleyiciye bir parametre olarak geçmeniz gerekir (veya #rectypes;;üst seviyeye girin ). Kabaca konuşma, bu birleşme sırasında "çek oluşur" bu kapanacaktır. Durum The type variable 'a occurs inside 'a -> 'bartık bir problem olmayacak. Tip sistemi hala "doğru" (ses vb.) Olacaktır, tür olarak ortaya çıkan sonsuz ağaçlara bazen "rasyonel ağaçlar" denir. Tip sistemi zayıflar, yani bazı programcı hatalarını tespit etmek imkansız hale gelir.

Benim Bkz lambda-hesap üzerinde ders OCaml içinde örneklerle fixpoint operatörleri hakkında ayrıntılı bilgi için (slayt 27 başlayarak).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.