जाँच और पुनरावर्ती प्रकार (Haskell / Ocaml में वाई कॉम्बीनेटर लिखना)


21

हास्केल के संदर्भ में वाई कॉम्बीनेटर को समझाते समय, यह आमतौर पर नोट किया जाता है कि अपने पुनरावर्ती प्रकार के कारण स्ट्रेट-फॉरवर्ड कार्यान्वयन हास्केल में टाइप-चेक नहीं करेगा।

उदाहरण के लिए, रोज़ेटाकोड से :

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))

और वास्तव में, "स्पष्ट" परिभाषा चेक टाइप नहीं करती है:

λ> 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)

Ocaml में एक ही सीमा मौजूद है:

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

हालाँकि, Ocaml में, कोई -rectypesस्विच में पास करके पुनरावर्ती प्रकारों की अनुमति दे सकता है :

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

का उपयोग करके -rectypes, सब कुछ काम करता है:

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

टाइप सिस्टम और प्रकार के अनुमान के बारे में उत्सुक होने के नाते, यह कुछ ऐसे प्रश्न उठाता है जिनका मैं अभी भी उत्तर नहीं दे पा रहा हूं।

  • सबसे पहले, प्रकार चेकर प्रकार के साथ कैसे आता है t2 = t2 -> t0 -> t1? उस प्रकार के साथ आने से, मुझे लगता है कि समस्या यह है कि प्रकार ( t2) सही पक्ष पर खुद को संदर्भित करता है?
  • दूसरा, और शायद सबसे दिलचस्प, हस्केल / ओकेमेल टाइप सिस्टमों को इसे खत्म करने का कारण क्या है? मुझे लगता है कि वहाँ है एक अच्छा कारण के बाद से OCaml भी डिफ़ॉल्ट भले ही यह द्वारा यह अनुमति नहीं दी जाएगी कर सकते हैं पुनरावर्ती प्रकार के साथ सौदा है, तो दिए गए -rectypesस्विच।

यदि ये वास्तव में बड़े विषय हैं, तो मैं प्रासंगिक साहित्य की ओर संकेत करता हूँ।

जवाबों:


16

सबसे पहले, जीएचसी त्रुटि,

जीएचसी कुछ बाधाओं को एकजुट करने का प्रयास कर रहा है x, सबसे पहले, हम इसे एक फ़ंक्शन के रूप में उपयोग करते हैं

x :: a -> b

अगला हम इसे उस फ़ंक्शन के मान के रूप में उपयोग करते हैं

x :: a

और अंत में हम इसे मूल तर्क अभिव्यक्ति के साथ एकीकृत करते हैं

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

अब x xएकजुट करने की कोशिश हो जाता है t2 -> t1 -> t0, फिर भी, हम इस को एकजुट नहीं कर सकते, क्योंकि यह एकीकृत की आवश्यकता होगी t2की, पहला तर्क xके साथ, x। इसलिए हमारी त्रुटि संदेश

अगला, सामान्य पुनरावर्ती प्रकार क्यों नहीं। वैसे ध्यान देने योग्य पहला बिंदु इक्वी और आईएसओ पुनरावर्ती प्रकार के बीच का अंतर है,

  • समान-पुनरावर्ती वही हैं जो आप अपेक्षा करेंगे कि mu X . Typeयह मनमाने ढंग से विस्तार या तह करने के बराबर है।
  • आइसो-पुनरावर्ती प्रकार ऑपरेटरों की एक जोड़ी प्रदान करते हैं, foldऔर unfoldजो प्रकार की पुनरावर्ती परिभाषाओं को मोड़ते और प्रकट करते हैं।

अब समान-पुनरावर्ती प्रकार ध्वनि आदर्श हैं, लेकिन जटिल प्रकार के सिस्टम में सही तरीके से प्राप्त करने के लिए बेतुका कठिन हैं। यह वास्तव में प्रकार की जाँच को अयोग्य बना सकता है। मैं OCaml के टाइप सिस्टम के हर विवरण से परिचित नहीं हूँ, लेकिन Haskell में पूरी तरह से सम-विषम प्रकार टाइपकार्ट को अनियंत्रित रूप से प्रकारों को एकजुट करने की कोशिश कर सकता है, डिफ़ॉल्ट रूप से, Haskell यह सुनिश्चित करता है कि टाइप चेकिंग समाप्त हो जाए। और अधिक, हास्केल में, समानार्थी शब्द गूंगे हैं, सबसे उपयोगी पुनरावर्ती प्रकारों को परिभाषित किया जाएगा type T = T -> (), हालांकि हास्केल में लगभग तुरंत इनबिल्ट किया जाता है, लेकिन आप एक पुनरावर्ती प्रकार को इनलाइन नहीं कर सकते हैं, यह अनंत है! इसलिए, हास्केल में पुनरावर्ती प्रकार एक विशाल ओवरहाल की मांग करेंगे कि समानार्थी कैसे संभाला जाता है, शायद भाषा विस्तार के रूप में भी डालने के प्रयास के लायक नहीं है।

इस्को-पुनरावर्ती प्रकार उपयोग करने के लिए एक दर्द का एक सा है, आपको कम या ज्यादा स्पष्ट रूप से टाइप चेकर को अपने प्रकारों को मोड़ना और अनफॉलो करना होगा, जिससे आपके कार्यक्रमों को पढ़ने और लिखने के लिए और अधिक जटिल हो जाएगा।

हालांकि, यह आपके Muप्रकार के साथ क्या कर रहा है, इसके समान है । Rollगुना है, और unrollसामने है। तो वास्तव में, हमारे पास iso-recursive प्रकार पके हुए हैं। हालाँकि, equi-recursive प्रकार अभी भी बहुत जटिल हैं, इसलिए OCaml और Haskell जैसी प्रणालियाँ आपको प्रकार के स्तर निर्धारण के माध्यम से पुनरावृत्ति पास करने के लिए बाध्य करती हैं।

अब अगर यह आपकी रूचि रखता है, तो मैं टाइप और प्रोग्रामिंग लैंग्वेज सुझाऊंगा। मेरी कॉपी मेरी गोद में खुली बैठी है क्योंकि मैं यह लिखने के लिए यह सुनिश्चित करने के लिए कि मुझे सही शब्दावली मिली है :)


विशेष अध्याय 21 में प्रेरण, सहवास, और पुनरावर्ती प्रकारों के लिए एक अच्छा अंतर्ज्ञान प्रदान करता है
डैनियल ग्रैज़र

धन्यवाद! यह वास्तव में आकर्षक है। मैं वर्तमान में टीएपीएल पढ़ रहा हूं, और मुझे यह सुनकर खुशी है कि इसे बाद में किताब में शामिल किया जाएगा।
बीटा

@ बीता हां, टीएपीएल और यह बड़े भाई, प्रकार और प्रोग्रामिंग भाषाओं में उन्नत विषय, अद्भुत संसाधन हैं।
डैनियल ग्रैजर

2

OCaml में, आपको -rectypesकंपाइलर के लिए एक पैरामीटर के रूप में पास करना होगा (या #rectypes;;toplevel में दर्ज करें )। मोटे तौर पर, यह एकीकरण के दौरान "चेक होता है" बंद हो जाएगा। स्थिति The type variable 'a occurs inside 'a -> 'bअब एक समस्या नहीं होगी। प्रकार प्रणाली अभी भी "सही" (ध्वनि, आदि) होगी, प्रकार के रूप में उत्पन्न होने वाले अनंत पेड़ों को कभी-कभी "तर्कसंगत पेड़" कहा जाता है। प्रकार प्रणाली कमजोर हो जाती है, अर्थात कुछ प्रोग्रामर त्रुटियों का पता लगाना असंभव हो जाता है।

OCaml में उदाहरणों के साथ फ़िक्सपॉइंट ऑपरेटरों के बारे में अधिक जानकारी के लिए लैम्ब्डा-कैलकुलस (स्लाइड 27 पर शुरू) पर मेरा व्याख्यान देखें ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.