हास्केल (कभी-कभी) को "सर्वश्रेष्ठ इंपीरियल भाषा" क्यों कहा जाता है?


84

(मुझे आशा है कि यह प्रश्न विषय-विषय है - मैंने उत्तर की खोज करने की कोशिश की, लेकिन निश्चित उत्तर नहीं मिला। यदि ऐसा ऑफ-टॉपिक हो या पहले से उत्तर दिया गया हो, तो कृपया इसे हटा दें / हटा दें।)

मुझे याद है कि हास्केल के बारे में आधा-चुटकुला टिप्पणी सुनने / पढ़ने के बाद कुछ समय के लिए सबसे अच्छा होना अनिवार्य भाषा है, जो निश्चित रूप से अजीब लगता है क्योंकि हास्केल आमतौर पर अपनी कार्यात्मक विशेषताओं के लिए जाना जाता है।

तो मेरा सवाल यह है कि हास्केल के कौन से गुण / विशेषताएं (यदि कोई हैं) तो हास्केल को सर्वश्रेष्ठ अनिवार्य भाषा समझने के औचित्य का कारण देती है - या क्या यह वास्तव में एक मजाक से अधिक है?


3
donsbot.wordpress.com/2007/03/10/… <- प्रोग्राम करने योग्य अर्धविराम।
विवि जियान

11
यह उद्धरण संभवतः अजीबोगरीब दस्ते से निपटने के परिचय के अंत से उत्पन्न होता है : हास्केल में मोनैडिक इनपुट / आउटपुट, संगामिति, अपवाद और विदेशी भाषा की कॉल जो कहती है "संक्षेप में, हास्केल दुनिया की imper घोंसला अनिवार्य प्रोग्रामिंग भाषा है।"
रसेल ओ'कॉनर

@ रसेल: उस कहावत का सबसे संभावित मूल (खुद SPJ होने के नाते) इंगित करने के लिए धन्यवाद!
hvr

आप हास्केल के साथ सख्त अत्याधिक
Janus Troelsen

जवाबों:


92

मैं इसे आधा सच मानता हूं। हास्केल में अमूर्त करने की अद्भुत क्षमता है, और इसमें अनिवार्य विचारों पर अमूर्तता भी शामिल है। उदाहरण के लिए, हास्केल के पास लूप होते समय कोई अंतर्निहित अनिवार्यता नहीं है, लेकिन हम इसे लिख सकते हैं और अब यह करता है:

while :: (Monad m) => m Bool -> m () -> m ()
while cond action = do
    c <- cond
    if c 
        then action >> while cond action
        else return ()

कई अनिवार्य भाषाओं के लिए अमूर्तता का यह स्तर कठिन है। यह अनिवार्य भाषाओं में किया जा सकता है जिनमें क्लोजर हैं; जैसे। अजगर और सी #।

लेकिन हास्केल में मोनाड कक्षाओं का उपयोग करते हुए अनुमत साइड-इफेक्ट को चिह्नित करने की अत्यधिक (अद्वितीय) क्षमता है । उदाहरण के लिए, यदि हमारे पास कोई फ़ंक्शन है:

foo :: (MonadWriter [String] m) => m Int

यह एक "जरूरी" कार्य हो सकता है, लेकिन हम जानते हैं कि यह केवल दो काम कर सकता है:

  • "आउटपुट" तार की एक धारा
  • एक Int लौटें

यह कंसोल पर प्रिंट नहीं कर सकता है या नेटवर्क कनेक्शन स्थापित नहीं कर सकता है, आदि को अमूर्त क्षमता के साथ जोड़कर, आप फ़ंक्शन लिख सकते हैं जो "किसी भी संगणना जो एक धारा का उत्पादन करता है" आदि पर कार्य करता है।

यह वास्तव में हास्केल की अमूर्त क्षमताओं के बारे में सब है जो इसे एक बहुत अच्छी अनिवार्य भाषा बनाता है।

हालांकि, झूठा आधा वाक्यविन्यास है। मुझे हस्केल सुंदर क्रिया और अजीब शैली में उपयोग करने के लिए अजीब लगता है। ऊपर दिए गए whileलूप का उपयोग करने के लिए यहां एक उदाहरण आवश्यक शैली की गणना है , जो एक लिंक की गई सूची के अंतिम तत्व को ढूंढती है:

lastElt :: [a] -> IO a
lastElt [] = fail "Empty list!!"
lastElt xs = do
    lst <- newIORef xs
    ret <- newIORef (head xs)
    while (not . null <$> readIORef lst) $ do
        (x:xs) <- readIORef lst
        writeIORef lst xs
        writeIORef ret x
    readIORef ret

सभी कि IORef कचरा, डबल रीड, <$>एक इनलाइन संगणना के परिणाम पर संचालित करने के लिए एक पढ़ने, fmapping ( ) के परिणाम को बांधने के लिए ... यह सब बहुत ही जटिल लग रही है। यह एक कार्यात्मक दृष्टिकोण से पूरी तरह से समझ में आता है, लेकिन अनिवार्य भाषाएं इनमें से अधिकांश विवरणों को गलीचा के नीचे स्वीप करती हैं ताकि उनका उपयोग करना आसान हो सके।

जाहिर है, शायद अगर हम एक अलग-अलग- whileशैली वाले कॉम्बिनेटर का उपयोग करते हैं तो यह क्लीनर होगा। लेकिन अगर आप उस दर्शन को पर्याप्त रूप से लेते हैं (स्पष्ट रूप से खुद को व्यक्त करने के लिए कॉम्बिनेटरों के एक समृद्ध सेट का उपयोग करके), तो आप फिर से कार्यात्मक प्रोग्रामिंग पर पहुंचते हैं। इम्पीरेटिव-स्टाइल हास्केल सिर्फ एक अच्छी तरह से डिज़ाइन की गई अनिवार्य भाषा की तरह "प्रवाह" नहीं करता है, जैसे कि अजगर।

अंत में, एक सिंटैक्टिक फेस-लिफ्ट के साथ, हास्केल सबसे अच्छी अनिवार्य भाषा हो सकती है। लेकिन, फेस लिफ्ट्स की प्रकृति से, यह आंतरिक रूप से सुंदर और वास्तविक के साथ बाहरी रूप से सुंदर और नकली कुछ की जगह लेगा।

संपादित करें : lastEltइस अजगर लिप्यंतरण के साथ विपरीत :

def last_elt(xs):
    assert xs, "Empty list!!"
    lst = xs
    ret = xs.head
    while lst:
        ret = lst.head
        lst = lst.tail
    return ret 

लाइनों की समान संख्या, लेकिन प्रत्येक पंक्ति में थोड़ा कम शोर है।


EDIT 2

यह किस लिए लायक है, इस तरह हास्केल में एक शुद्ध प्रतिस्थापन दिखता है:

lastElt = return . last

बस। या, यदि आप मुझे उपयोग करने से मना करते हैं Prelude.last:

lastElt [] = fail "Unsafe lastElt called on empty list"
lastElt [x] = return x
lastElt (_:xs) = lastElt xs

या, यदि आप चाहते हैं कि यह किसी भी Foldableडेटा संरचना पर काम करे और यह पहचानें कि आपको वास्तव में त्रुटियों को संभालने की आवश्यकता नहीं है IO:

import Data.Foldable (Foldable, foldMap)
import Data.Monoid (Monoid(..), Last(..))

lastElt :: (Foldable t) => t a -> Maybe a
lastElt = getLast . foldMap (Last . Just)

साथ Map, उदाहरण के लिए:

λ➔ let example = fromList [(10, "spam"), (50, "eggs"), (20, "ham")] :: Map Int String
λ➔ lastElt example
Just "eggs"

(.)ऑपरेटर है समारोह रचना


2
आप अधिक सार बनाकर आईओआरएफ के शोर को बहुत कम कष्टप्रद बना सकते हैं।
ss11

1
@ उद्घाटन, हम्म, मैं इसके बारे में उत्सुक हूं। क्या आपका मतलब है अधिक डोमेन-स्तरीय अमूर्तता, या केवल एक समृद्ध अनिवार्यता का निर्माण करके " । कार्यात्मक से जोड़) पिछले के लिए, मैं वास्तव में आप क्या मतलब है यह देखने के लिए, क्योंकि मैं नहीं सोच सकते हैं दिलचस्पी होगी कैसे मेरे सिर के ऊपर से।
luqui

2
@luqui एसटी का उपयोग करने की अनुमति दी गई साइड इफेक्ट को चिह्नित करने के लिए एक अच्छा उदाहरण होगा। एक बोनस के रूप में एसटी से एक शुद्ध गणना में वापस कूदना संभव है।
फ़ूज

5
तुलना के रूप में पायथन का उपयोग करना पूरी तरह से उचित नहीं है - जैसा कि आप कहते हैं, यह अच्छी तरह से डिज़ाइन किया गया है, जो कि मैं सबसे अच्छी तरह से स्पष्ट होने वाली अनिवार्य भाषाओं में से एक हूं। समान तुलना का तर्क होगा कि अधिकांश अनिवार्य भाषाएं एक अनिवार्य शैली में उपयोग करने के लिए अजीब हैं ... हालांकि, शायद यह वही है जो आप का मतलब है। ;]
सीए मैककैन

5
बातचीत के लिए एक फुटनोट, पोस्टरिटी केIORef लिए: @ उद्घाटन को तदर्थ बहुरूपता का उपयोग करते हुए, एसएचसी को निहित करने के लिए , या कम से कम जीएचसी में परिवर्तन करके इसे विफल करने का प्रयास किया जा रहा है। : [
सीए मैककैन

22

यह मजाक नहीं है, और मुझे विश्वास है। मैं इसे उन लोगों के लिए सुलभ रखने का प्रयास करूँगा जो किसी हास्केल को नहीं जानते हैं। हास्केल डू-नोटेशन (अन्य चीजों के अलावा) का उपयोग करने के लिए आपको अनिवार्य कोड लिखने के लिए अनुमति देता है (हाँ, यह मोनड्स का उपयोग करता है, लेकिन इसके बारे में चिंता न करें)। यहाँ कुछ फायदे हैं जो हास्केल आपको देता है:

  • सबरूटीन का आसान निर्माण। मान लीजिए कि मैं स्टैडआउट और स्टेडर के लिए एक मान प्रिंट करना चाहता हूं। मैं निम्नलिखित लिख सकता हूं, एक छोटी पंक्ति के साथ सबरूटीन को परिभाषित करना:

    do let printBoth s = putStrLn s >> hPutStrLn stderr s
       printBoth "Hello"
       -- Some other code
       printBoth "Goodbye"
    
  • चारों ओर कोड पारित करने के लिए आसान है। यह देखते हुए कि मैंने ऊपर लिखा है, अगर मैं अब printBothफ़ंक्शन का उपयोग स्ट्रिंग्स की सभी सूची को प्रिंट करने के लिए करना चाहता हूं, तो यह आसानी से mapM_फ़ंक्शन के लिए मेरे सबरूटीन पास करके किया जाता है :

    mapM_ printBoth ["Hello", "World!"]
    

    एक और उदाहरण, हालांकि जरूरी नहीं है, छँटाई है। मान लें कि आप स्ट्रिंग्स को केवल लंबाई के आधार पर क्रमबद्ध करना चाहते हैं। तुम लिख सकते हो:

    sortBy (\a b -> compare (length a) (length b)) ["aaaa", "b", "cc"]
    

    जो आपको ["b", "cc", "aaaa"] देगा। (आप इसे उससे भी छोटा लिख ​​सकते हैं, लेकिन अभी के लिए कभी भी बुरा मत मानना।)

  • कोड का फिर से उपयोग करना आसान है। वह mapM_फ़ंक्शन बहुत उपयोग किया जाता है, और अन्य भाषाओं में प्रत्येक लूप की जगह लेता है। वहाँ भी है foreverजो थोड़ी देर (सच) की तरह काम करता है, और विभिन्न अन्य कार्य जो कोड पारित किए जा सकते हैं और इसे विभिन्न तरीकों से निष्पादित कर सकते हैं। इसलिए अन्य भाषाओं में छोरों को हास्केल में इन नियंत्रण कार्यों द्वारा प्रतिस्थापित किया जाता है (जो विशेष नहीं हैं - आप उन्हें बहुत आसानी से परिभाषित कर सकते हैं)। सामान्य तौर पर यह लूप की स्थिति को गलत करना मुश्किल बनाता है, ठीक उसी तरह जैसे कि प्रत्येक लूप के लिए लंबे समय तक चलने वाले इटरेटर समतुल्य (जैसे जावा), या एरे-इंडेक्सिंग लूप्स (जैसे C) में गलत होना कठिन होता है।

  • बाइंडिंग असाइनमेंट नहीं। मूल रूप से, आप केवल एक बार एक चर को असाइन कर सकते हैं (बल्कि एकल स्थैतिक असाइनमेंट की तरह)। यह किसी भी बिंदु पर एक चर के संभावित मूल्यों के बारे में बहुत भ्रम को दूर करता है (इसका मूल्य केवल एक पंक्ति में सेट किया गया है)।
  • निहित दुष्प्रभाव। मान लीजिए कि मैं स्टड से एक पंक्ति पढ़ना चाहता हूं, और इसे कुछ फ़ंक्शन लागू करने के बाद इसे stdout पर लिखना चाहता हूं (हम इसे चिड़ियाघर कहेंगे)। तुम लिख सकते हो:

    do line <- getLine
       putStrLn (foo line)
    

    मुझे तुरंत पता है कि fooइसका कोई अप्रत्याशित दुष्प्रभाव नहीं है (जैसे कि वैश्विक वैरिएबल को अपडेट करना, या मेमोरी को डील करना, या जो भी हो), क्योंकि यह टाइप होना चाहिए स्ट्रिंग -> स्ट्रिंग, जिसका अर्थ है कि यह एक शुद्ध कार्य है; जो भी मूल्य मैं पास करता हूं, उसे हर बार एक ही परिणाम वापस करना चाहिए, बिना दुष्प्रभाव के। हास्केल अच्छी तरह से साइड-कोडिंग कोड को शुद्ध कोड से अलग करता है। C, या Java जैसी किसी चीज़ में, यह स्पष्ट नहीं है (यह getFoo () विधि में परिवर्तन करता है? आपको उम्मीद नहीं होगी, लेकिन यह हो सकता है ...)।

  • कचरा इकठा करना। इन दिनों बहुत सी भाषाएं कचरा एकत्र की जाती हैं, लेकिन ध्यान देने योग्य हैं: स्मृति को आवंटित करने और डील करने का कोई झंझट नहीं।

इसके अलावा शायद कुछ और फायदे हैं, लेकिन वे हैं जो दिमाग में आते हैं।


9
मैं मजबूत प्रकार की सुरक्षा में जोड़ूंगा। हास्केल कंपाइलर को त्रुटियों की एक बड़ी श्रेणी को खत्म करने की अनुमति देता है। हाल ही में कुछ जावा कोड पर काम करने के बाद, मुझे याद दिलाया गया कि कैसे भयानक अशक्त बिंदु हैं और कितने प्रकार के बिना ओओपी गायब है।
माइकल स्नॉयमैन

1
आपके विस्तार के लिए धन्यवाद! आपके उल्लेखित फायदे हास्केल को 'प्रथम श्रेणी की वस्तुओं (जो इस तरह से दहनशील हैं) को एक साथ सीमांकित दायरे में उन प्रभावों को' 'समाहित' 'करने की क्षमता के रूप में' 'अनिवार्य' 'प्रभावों के साथ उबालने लगते हैं। क्या यह एक पर्याप्त संकुचित सारांश है?
hvr

19
@ मायिक स्नोमैन: लेकिन ओओपी में योग के प्रकार आसान हैं! बस एक अमूर्त वर्ग को परिभाषित करें जो आपके डेटा प्रकार के चर्च एन्कोडिंग का प्रतिनिधित्व करता है, मामलों के लिए उपवर्ग, उन वर्गों के लिए इंटरफेस जो प्रत्येक मामले को संसाधित कर सकते हैं, और फिर ऑब्जेक्ट्स को पास कर सकते हैं जो प्रत्येक वस्तु को एक सम वस्तु में समर्थन करते हैं, नियंत्रण प्रवाह के लिए उपप्रकार पॉलीमोफिज़्म का उपयोग करते हुए (जैसे तुम्हे करना चाहिए)। सरल नहीं हो सकता। आप डिजाइन पैटर्न से नफरत क्यों करते हैं?
सीए मैक्कैन

9
@camccann मुझे पता है कि आप मजाक कर रहे हैं, लेकिन यह अनिवार्य रूप से वही है जो मैंने अपने प्रोजेक्ट में लागू किया है।
बजे माइकल स्नॉयमैन

9
@ मिचेल स्नोमैन: अच्छा विकल्प, फिर! असली मजाक यह है कि मैं वर्णन कर रहा था कि एक मजाक की तरह लगने वाले तरीके में सबसे अच्छा एन्कोडिंग क्या है। हा, हा! फांसी के लिए सभी तरह से हंसते हुए ...
सीए मैक्कैन

17

पहले से ही उल्लेख किया गया है, जो अन्य के पहले उल्लेख किया गया है के अलावा, कभी-कभी कार्रवाई उपयोगी हो सकता है। विचार दिखाने के लिए यहां एक मूर्खतापूर्ण उदाहरण दिया गया है:

f = sequence_ (reverse [print 1, print 2, print 3])

यह उदाहरण दिखाता है कि आप साइड-इफेक्ट्स (इस उदाहरण में print) के साथ गणना कैसे बना सकते हैं और फिर डेटा संरचनाओं में डाल सकते हैं या उन्हें अन्य तरीकों से हेरफेर कर सकते हैं, वास्तव में उन्हें निष्पादित करने से पहले।


1
मुझे लगता है कि जावास्क्रिप्ट कोड जो इस से मेल खाता है वह होगा call = x => x(); sequence_ = xs => xs.forEach(call) ;print = console.log; f = () => sequence_([()=> print(1), () => print(2), () => print(3)].reverse()):। मुख्य अंतर जो मैं देख रहा हूं वह यह है कि हमें कुछ अतिरिक्त की आवश्यकता है () =>
हेजल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.