2012-11-26 11 views
15

मैं समझने के लिए संघर्ष कर रहा हूं कि इन दो स्निपेट तथाकथित "गरीब व्यक्ति के सख्त विश्लेषण" के तहत अलग-अलग परिणाम क्यों उत्पन्न करते हैं।डेटा और नए प्रकार के बीच आलस्य/कठोरता

पहला उदाहरण का उपयोग करता है data (एक सही अनुप्रयोगी उदाहरण मानते हुए):

data Parser t a = Parser { 
     getParser :: [t] -> Maybe ([t], a) 
    } 

> getParser (pure (,) <*> literal ';' <*> undefined) "abc" 
*** Exception: Prelude.undefined 

दूसरा newtype उपयोग करता है। वहाँ कोई अन्य अंतर है:

newtype Parser t a = Parser { 
     getParser :: [t] -> Maybe ([t], a) 
    } 

> getParser (pure (,) <*> literal ';' <*> undefined) "abc" 
Nothing 

literal x एक पार्सर कि इनपुट में से एक टोकन लेने वाली सफल होता है अपने तर्क पहले टोकन से मेल खाता है। तो इस उदाहरण में, यह ; से a से मेल नहीं खाता है। हालांकि, data उदाहरण अभी भी देखता है कि अगला पार्सर अपरिभाषित है, जबकि newtype उदाहरण नहीं है।

मैंने, this, और this पढ़ा है, लेकिन यह समझने के लिए पर्याप्त नहीं है कि पहला उदाहरण क्यों अनिर्धारित है। ऐसा लगता है कि इस उदाहरण में, newtypedata से आलसी है, उत्तर के विपरीत के विपरीत। (कम से कम one other person भी इससे उलझन में है)।

data से newtype पर स्विचिंग क्यों इस उदाहरण की परिभाषा को बदलती है?


यहाँ एक और बात यह है कि मुझे पता चला है: इस अनुप्रयोगी उदाहरण के साथ, आउटपुट ऊपर data पार्सर अपरिभाषित:

instance Applicative (Parser s) where 
    Parser f <*> Parser x = Parser h 
    where 
     h xs = 
     f xs >>= \(ys, f') -> 
     x ys >>= \(zs, x') -> 
     Just (zs, f' x') 

    pure a = Parser (\xs -> Just (xs, a)) 

जबकि इस उदाहरण के साथ, data पार्सर ऊपर करता नहीं उत्पादन अपरिभाषित (यह मानते हुए Parser s के लिए एक सही मोनाड उदाहरण):

instance Applicative (Parser s) where 
    f <*> x = 
     f >>= \f' -> 
     x >>= \x' -> 
     pure (f' x') 

    pure = pure a = Parser (\xs -> Just (xs, a)) 

पूर्ण कोड स्निपेट:

import Control.Applicative 
import Control.Monad (liftM) 

data Parser t a = Parser { 
     getParser :: [t] -> Maybe ([t], a) 
    } 


instance Functor (Parser s) where 
    fmap = liftM 

instance Applicative (Parser s) where 
    Parser f <*> Parser x = Parser h 
    where 
     h xs = f xs >>= \(ys, f') -> 
     x ys >>= \(zs, x') -> 
     Just (zs, f' x') 

    pure = return 


instance Monad (Parser s) where 
    Parser m >>= f = Parser h 
    where 
     h xs = 
      m xs >>= \(ys,y) -> 
      getParser (f y) ys 

    return a = Parser (\xs -> Just (xs, a)) 


literal :: Eq t => t -> Parser t t 
literal x = Parser f 
    where 
    f (y:ys) 
     | x == y = Just (ys, x) 
     | otherwise = Nothing 
    f [] = Nothing 
+2

इस तरह के एक प्रश्न पूछते समय, यह सामान्य रूप से बेहतर होता है यदि आप सभी प्रासंगिक कोड शामिल करते हैं, यदि यह फिट करने के लिए पर्याप्त छोटा है (इसमें 'फंक्टर' और 'मोनाड' उदाहरण, और 'शाब्दिक' शामिल हैं), ताकि लोग डॉन करें यह अनुमान लगाने की ज़रूरत नहीं है कि आपने कार्यों को कैसे लिखा है (जैसा कि आपने बताया है, यहां तक ​​कि छोटे बदलाव भी व्यवहार में अंतर डाल सकते हैं)। – shachaf

+1

@shachaf असली सवाल यहां नहीं है "मैं अपना कोड कैसे ठीक करूं?" - मैंने पहले ही ऐसा किया है - लेकिन "सख्तता/आलस्य के संबंध में 'डेटा' और 'न्यूटाइप' के बीच क्या अंतर है?" क्षमा करें अगर यह सवाल से स्पष्ट नहीं है। –

+0

हां, लेकिन फिर भी, हम यह बताए बिना कोड को कैसा दिखने के बिना आपके कोड के साथ क्या चल रहा था? – shachaf

उत्तर

18

आप शायद जानते हैं, data और newtype के बीच मुख्य अंतर यह है कि data साथ है कि data कंस्ट्रक्टर्स आलसी हैं, जबकि newtype सख्त है, यानी निम्नलिखित प्रकार

दिया है
data D a = D a 
newtype N a = N a 

तब D ⊥ `seq` x = x, लेकिन N ⊥ `seq` x = ⊥

क्या शायद कम सामान्यतः जाना जाता है, हालांकि, यह है कि जब इन निर्माताओं पर आप पैटर्न मैच, भूमिकाओं

constD x (D y) = x 
constN x (N y) = x 

तो constD x ⊥ = ⊥, लेकिन constN x ⊥ = x के साथ "उलट" कर रहे हैं, अर्थात।

यह आपके उदाहरण में क्या हो रहा है।

Parser f <*> Parser x = Parser h where ... 
data साथ

, <*> की परिभाषा में पैटर्न मैच तुरंत अगर तर्क के दोनों हैं वितरित हो जाएगा, लेकिन newtype साथ कंस्ट्रक्टर्स अनदेखी कर रहे हैं और यह है जैसे कि आपने लिखा था

f <*> x = h where 

जो की मांग है तो केवल x = ⊥ के लिए अलग हो जाएगा।

+0

दो चीजें जो मैं अभी भी बिल्कुल स्पष्ट नहीं हूं 1) क्या पैटर्न मिलान अंतर को हास्केल के अर्थशास्त्र द्वारा जरूरी है, और 2) क्या पैटर्न मिलान अंतर कन्स्ट्रक्टर के सख्त अंतर के कारण है। –

+0

@ मैटफ़ेनविक: 1) हां, चूंकि न्यूटाइप मूल रूप से मौजूद नहीं हैं, इसलिए एक पर मिलान पैटर्न पैटर्न के आधार पर पैटर्न के समान होता है, इसलिए पैटर्न 'x'' x' का मूल्यांकन नहीं करता है, न ही करता है पैटर्न 'एन एक्स'। 2) नहीं। डेटा एस ए = एस! ए पर विचार करें; कॉन्स एक्स (एस वाई) = एक्स', फिर '' 'एस अपरिभाषित 'seq' x = ⊥''', और' constS x ⊥ = ⊥'। – hammar

+0

तो डेटा कन्स्ट्रक्टर के मामले में कंपाइलर को यह निर्धारित करने के लिए पर्याप्त मूल्यांकन करना होगा कि कन्स्ट्रक्टर पैटर्न से मेल खाता है या नहीं? –

10

data और newtype के बीच का अंतर यह है कि data "उठाया गया" और newtype नहीं है। इसका मतलब है कि data में अतिरिक्त ⊥ है - इस मामले में, इसका मतलब है कि undefined/= Parser undefined। जब Parser x पर कोड पैटर्न-मैचों, तो यह कन्स्ट्रक्टर के मान को मजबूर करता है।

जब आप data कन्स्ट्रक्टर पर पैटर्न-मिलान करते हैं, तो इसका मूल्यांकन किया जाता है और यह सुनिश्चित करने के लिए अलग किया जाता है कि यह ⊥ नहीं है। उदाहरण के लिए:

λ> data Foo = Foo Int deriving Show 
λ> case undefined of Foo _ -> True 
*** Exception: Prelude.undefined 

तो एक data निर्माता पर पैटर्न मिलान सख्त है, और यह बाध्य करेगा। दूसरी ओर, newtype, उसी तरह से प्रदर्शित होता है जैसे कि इसके कन्स्ट्रक्टर लपेटता है। तो एक newtype निर्माता पर मिलान बिल्कुल कुछ नहीं करता है:

λ> newtype Foo = Foo Int deriving Show 
λ> case undefined of Foo _ -> True 
True 

संभवतः अपने data कार्यक्रम इस तरह है कि यह दुर्घटना नहीं करता बदलने के लिए दो तरीके हैं। एक आपके Applicative उदाहरण में एक अपरिवर्तनीय पैटर्न मिलान का उपयोग करना होगा, जो हमेशा "सफल" होगा (लेकिन मिलान किए गए मानों का उपयोग करके कहीं भी असफल हो सकता है)। प्रत्येक newtype मिलान एक अचूक पैटर्न की तरह व्यवहार करता है (क्योंकि सख्तता के अनुसार मिलान करने के लिए कोई कन्स्ट्रक्टर नहीं है)।

λ> data Foo = Foo Int deriving Show 
λ> case undefined of ~(Foo _) -> True 
True 

अन्य undefined के बजाय Parser undefined उपयोग करने के लिए होगा:

λ> case Foo undefined of Foo _ -> True 
True 

इस मैच सफल होगा, क्योंकि वहाँ एक वैध Foo मूल्य कि पर मिलान किया जा रहा है। यह undefined निहित होता है, लेकिन यह प्रासंगिक नहीं है क्योंकि हम इसका उपयोग नहीं करते हैं - हम केवल शीर्षतम कन्स्ट्रक्टर को देखते हैं।


सभी लिंक आप दे दी है के अलावा, आप this article प्रासंगिक हो सकता है।

संबंधित मुद्दे