क्षमा करें, मैं वास्तव में अपने गणित को नहीं जानता हूं, इसलिए मैं उत्सुक हूं कि एप्लास्टिक टाइपकास्ट में कार्यों का उच्चारण कैसे किया जाए
मुझे लगता है कि आपके गणित को जानना, काफी हद तक अप्रासंगिक है। जैसा कि आप शायद जानते हैं, हास्केल सार गणित के विभिन्न क्षेत्रों से शब्दावली के कुछ बिट्स उधार लेते हैं, विशेष रूप से श्रेणी सिद्धांत , जहां से हमें फंक्शनलर्स और मोनाड्स मिलते हैं। हास्केल में इन शब्दों का उपयोग कुछ हद तक औपचारिक गणितीय परिभाषाओं से भिन्न होता है, लेकिन वे आमतौर पर पर्याप्त रूप से अच्छे वर्णनात्मक शब्द हो सकते हैं।
Applicative
प्रकार वर्ग के बीच में कहीं बैठता है Functor
और Monad
, इसलिए एक यह एक ऐसी ही गणितीय आधार है की उम्मीद करेंगे। Control.Applicative
मॉड्यूल के लिए प्रलेखन के साथ शुरू होता है:
यह मॉड्यूल एक फ़न्क्टर और एक सनक के बीच एक संरचना मध्यवर्ती का वर्णन करता है: यह शुद्ध अभिव्यक्ति और अनुक्रमण प्रदान करता है, लेकिन कोई बंधन नहीं। (तकनीकी रूप से, एक मजबूत लक्ष्मण मोनोएडल फंक्टर है।)
हम्म।
class (Functor f) => StrongLaxMonoidalFunctor f where
. . .
Monad
मुझे लगता है कि काफी आकर्षक नहीं है।
यह सब मूल रूप से उबलता है, जो कि Applicative
किसी भी अवधारणा के अनुरूप नहीं है जो विशेष रूप से गणितीय रूप से दिलचस्प है, इसलिए कोई भी तैयार-किए गए शब्द नहीं हैं जो कि हास्केल में उपयोग किए जाने वाले तरीके पर कब्जा करते हैं। तो, अब के लिए गणित अलग सेट करें।
अगर हम (<*>)
यह जानना चाहते हैं कि इसे क्या कहा जाए तो यह जानने में मदद मिल सकती है कि इसका मूल अर्थ क्या है।
तो क्या साथ हो रहा है Applicative
वैसे भी, और क्यों करते हैं हम यह है कि कहते हैं?
Applicative
व्यवहार में कितनी मात्रा में मनमाना कार्यों को उठाने का एक तरीका है Functor
। के संयोजन पर विचार करें Maybe
(यकीनन सबसे सरल गैर-तुच्छ Functor
) और Bool
(इसी तरह सबसे सरल गैर-तुच्छ डेटा प्रकार)।
maybeNot :: Maybe Bool -> Maybe Bool
maybeNot = fmap not
फंक्शन fmap
हमें not
काम करने से Bool
लेकर काम करने तक की सुविधा देता है Maybe Bool
। लेकिन अगर हम उठाना चाहते हैं तो क्या होगा (&&)
?
maybeAnd' :: Maybe Bool -> Maybe (Bool -> Bool)
maybeAnd' = fmap (&&)
ठीक है, यह वही नहीं है जो हम चाहते हैं ! वास्तव में, यह बहुत बेकार है। हम चतुर होने की कोशिश कर सकते हैं और पीछे से दूसरे Bool
में घुस सकते हैं Maybe
...
maybeAnd'' :: Maybe Bool -> Bool -> Maybe Bool
maybeAnd'' x y = fmap ($ y) (fmap (&&) x)
... लेकिन यह अच्छा नहीं है। एक बात के लिए, यह गलत है। एक और बात के लिए, यह बदसूरत है । हम कोशिश कर सकते हैं, लेकिन यह पता चलता है कि मनमाने ढंग से काम करने के लिए कई तर्कों के एक समारोह को उठानेFunctor
का कोई तरीका नहीं है । कष्टप्रद!
दूसरी ओर, हम इसे आसानी से कर सकता है अगर हम इस्तेमाल किया Maybe
है Monad
उदाहरण:
maybeAnd :: Maybe Bool -> Maybe Bool -> Maybe Bool
maybeAnd x y = do x' <- x
y' <- y
return (x' && y')
जिसके कारण - अब, यह परेशानी का एक बहुत बस एक साधारण समारोह का अनुवाद करने में है Control.Monad
, यह स्वतः ही करने के लिए एक समारोह प्रदान करता है liftM2
। इसके नाम में 2 इस तथ्य को संदर्भित करता है कि यह ठीक दो तर्कों के कार्यों पर काम करता है; समान कार्य 3, 4 और 5 तर्क कार्यों के लिए मौजूद हैं। ये कार्य बेहतर हैं , लेकिन सही नहीं हैं, और तर्कों की संख्या बदसूरत और अनाड़ी है।
जो हमें उस पेपर पर लाता है जिसने एप्लिकेशन टाइप क्लास की शुरुआत की थी । इसमें, लेखक अनिवार्य रूप से दो अवलोकन करते हैं:
- बहु-तर्क कार्यों को उठाना
Functor
एक बहुत ही स्वाभाविक बात है
- ऐसा करने से पूर्ण क्षमताओं की आवश्यकता नहीं होती है
Monad
सामान्य फ़ंक्शन एप्लिकेशन को सरल शब्दों के शब्दों में लिखा जाता है, इसलिए "उठा हुआ आवेदन" को जितना संभव हो उतना सरल और प्राकृतिक बनाने के लिए, पेपर infix ऑपरेटरों को आवेदन के लिए खड़े होने के लिएFunctor
परिचय देता है , और इसे टाइप करने के लिए जो आवश्यक है, प्रदान करने के लिए एक प्रकार वर्ग। ।
जिनमें से सभी हमें निम्न बिंदु पर लाते हैं: (<*>)
बस फ़ंक्शन अनुप्रयोग का प्रतिनिधित्व करता है - इसलिए आप इसे व्हाट्सएप "जूसकैप्सिशन ऑपरेटर" की तुलना में किसी भी अलग उच्चारण क्यों करते हैं?
लेकिन अगर यह बहुत संतोषजनक नहीं है, तो हम देख सकते हैं कि Control.Monad
मॉड्यूल एक फ़ंक्शन भी प्रदान करता है जो मोनड्स के लिए एक ही काम करता है:
ap :: (Monad m) => m (a -> b) -> m a -> m b
कहाँ ap
"लागू" का संक्षिप्त रूप ज़ाहिर है, है। चूंकि कोई भी Monad
हो सकता है Applicative
, और ap
बाद में मौजूद सुविधाओं के केवल सबसेट की जरूरत होती है, हम शायद यह कह सकते हैं कि यदि (<*>)
कोई ऑपरेटर नहीं था, तो उसे बुलाया जाना चाहिए ap
।
हम दूसरी दिशा से भी चीजों को देख सकते हैं। Functor
उठाने आपरेशन कहा जाता है fmap
, क्योंकि यह का सामान्यीकरण है map
सूचियों पर आपरेशन। सूचियों पर किस तरह का फ़ंक्शन काम करेगा (<*>)
? वहाँ ap
सूचियों पर क्या है, ज़ाहिर है, लेकिन यह अपने आप में विशेष रूप से उपयोगी नहीं है।
वास्तव में, सूचियों के लिए शायद अधिक प्राकृतिक व्याख्या है। निम्न प्रकार के हस्ताक्षर को देखते समय आपके मन में क्या आता है?
listApply :: [a -> b] -> [a] -> [b]
वहाँ कुछ तो बस सूची में अस्तर के विचार के बारे में आकर्षक है, दूसरे के पहले तत्व में प्रत्येक फ़ंक्शन को लागू करना। दुर्भाग्य से हमारे पुराने दोस्त के लिए Monad
, यह सरल ऑपरेशन मोनड कानूनों का उल्लंघन करता है यदि सूचियां अलग-अलग लंबाई की हैं। लेकिन यह एक ठीक है Applicative
, जिस स्थिति में एक सामान्यीकृत संस्करण को एक साथ स्ट्रिंग(<*>)
करने का एक तरीका बन जाता है , तो शायद हम इसे कॉल करने की कल्पना कर सकते हैं ?zipWith
fzipWith
यह ज़िपिंग विचार वास्तव में हमें पूर्ण चक्र लाता है। उस गणित की सामग्री को पहले याद करें, मोनोएडल फंक्शनलर्स के बारे में? जैसा कि नाम से ही पता चलता है, ये मोनॉयड और फंक्शनलर्स की संरचना के संयोजन का एक तरीका है, जो दोनों परिचित हास्केल प्रकार वर्ग हैं:
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Monoid a where
mempty :: a
mappend :: a -> a -> a
यदि आप उन्हें एक साथ एक बॉक्स में रखते हैं और इसे थोड़ा ऊपर हिलाते हैं तो ये कैसा दिखेगा? से Functor
हम एक के विचार रखेंगे अपने प्रकार पैरामीटर की संरचना स्वतंत्र से, और Monoid
हम कार्यों के समग्र रूप रखेंगे:
class (Functor f) => MonoidalFunctor f where
mfEmpty :: f ?
mfAppend :: f ? -> f ? -> f ?
हम यह नहीं मानना चाहते हैं कि वास्तव में "खाली" बनाने का एक तरीका है Functor
, और हम एक मनमाने प्रकार के मूल्य को जोड़ नहीं सकते हैं, इसलिए हम इसके प्रकार को ठीक mfEmpty
करेंगे f ()
।
हम भी mfAppend
एक निरंतर प्रकार के पैरामीटर की आवश्यकता के लिए बाध्य नहीं करना चाहते हैं , इसलिए अब हमारे पास यह है:
class (Functor f) => MonoidalFunctor f where
mfEmpty :: f ()
mfAppend :: f a -> f b -> f ?
परिणाम प्रकार किसके लिए है mfAppend
? हमारे पास दो मनमाने प्रकार हैं जिनके बारे में हमें कुछ नहीं पता है, इसलिए हमारे पास कई विकल्प नहीं हैं। सबसे समझदार बात दोनों को रखना है:
class (Functor f) => MonoidalFunctor f where
mfEmpty :: f ()
mfAppend :: f a -> f b -> f (a, b)
जिस बिंदु mfAppend
पर अब स्पष्ट रूप से zip
सूचियों का एक सामान्यीकृत संस्करण है , और हम Applicative
आसानी से पुनर्निर्माण कर सकते हैं:
mfPure x = fmap (\() -> x) mfEmpty
mfApply f x = fmap (\(f, x) -> f x) (mfAppend f x)
यह हमें दिखाता है कि pure
किसी के पहचान तत्व से संबंधित है Monoid
, इसलिए इसके लिए अन्य अच्छे नाम एक इकाई मूल्य, एक अशक्त ऑपरेशन, या ऐसा कुछ भी सुझा सकते हैं।
यह लंबा था, इसलिए संक्षेप में:
(<*>)
केवल एक संशोधित फ़ंक्शन अनुप्रयोग है, इसलिए आप इसे या तो "एपी" या "लागू करें" के रूप में पढ़ सकते हैं, या इसे पूरी तरह से उसी तरह से लागू कर सकते हैं जिस तरह से आप फ़ंक्शन को सामान्य करेंगे।
(<*>)
zipWith
सूचियों पर भी मोटे तौर पर सामान्यीकरण किया जाता है, इसलिए आप इसे "ज़िप फंपर के साथ" के रूप में पढ़ सकते हैं, इसी तरह fmap
"मैप विथ ए फक्टर" के रूप में पढ़ने के लिए ।
पहला Applicative
प्रकार वर्ग के इरादे के करीब है - जैसा कि नाम से पता चलता है - इसलिए यही मैं सुझाता हूं।
वास्तव में, मैं उदारवादी उपयोग और गैर-उच्चारण को प्रोत्साहित करता हूं , जो सभी उठाए गए आवेदन ऑपरेटरों के हैं :
(<$>)
, जो एकल-तर्क कार्य को एक में बदल देता है Functor
(<*>)
, जो एक बहु-तर्क फ़ंक्शन के माध्यम से श्रृंखला बनाते हैं Applicative
(=<<)
, जो Monad
एक मौजूदा संगणना पर प्रवेश करने वाले फ़ंक्शन को बांधता है
सभी तीनों, दिल से, बस नियमित रूप से काम करने वाले अनुप्रयोग हैं, थोड़ा सा मसालेदार।