2013-04-22 6 views
19

तीर हास्केल समुदाय में लोकप्रियता प्राप्त कर रहे हैं, लेकिन ऐसा लगता है जैसे मोनाड्स अधिक शक्तिशाली हैं। तीरों का उपयोग करके क्या प्राप्त किया जाता है? इसके बजाय मोनाड्स का उपयोग क्यों नहीं किया जा सकता है?तीर क्या कर सकते हैं जो मोनाड्स नहीं कर सकते? प्रतीत होता है कि

+0

यह एक अच्छा स्टैक ओवरफ़्लो प्रश्न होने के लिए थोड़ा सा व्यापक है। क्या आप इसे और अधिक विशिष्ट बना सकते हैं? –

+8

"हास्कल समुदाय में तीर लोकप्रियता प्राप्त कर रहे हैं" - यह वास्तव में मेरी छाप नहीं है; लगता है कि तीर एक मृत अंत AFAICT के रूप में बाहर निकला है। उम्मीद है कि टुकड़े श्रेणी के चारों ओर बनाए गए कुछ और शक्तिशाली और उपयोगी abstractions के लिए एक साथ आएंगे और मानकीकृत बन जाएगा। – jberryman

उत्तर

11

मुझे इन शर्तों में इस मुद्दे के बारे में सोचना मुश्किल लगता है: तीरों का उपयोग करके प्राप्त किया जाता है। जैसा कि अन्य टिप्पणीकारों ने उल्लेख किया है, हर मोनड को छोटे से तीर में बदल दिया जा सकता है। तो एक monad सभी तीर-वाई चीजें कर सकते हैं। हालांकि, हम तीर बना सकते हैं जो मोनैड नहीं हैं। ऐसा कहने के लिए, हम ऐसे प्रकार बना सकते हैं जो इन तीर-वाई चीजों को मोनैडिक बाइंडिंग का समर्थन किए बिना कर सकते हैं। यह मामला जैसा प्रतीत नहीं होता है, लेकिन मोनैडिक बाइंड फ़ंक्शन वास्तव में एक बहुत ही सीमित (इसलिए शक्तिशाली) ऑपरेशन है जो कई प्रकार के अयोग्यता को अक्षम करता है।

देखें, बांधने के लिए, आपको यह कहने में सक्षम होना चाहिए कि इनपुट प्रकार के बावजूद, बाहर आने वाला क्या है, मोनैड में लपेटा जा रहा है।

(>>=) :: forall a b. m a -> (a -> m b) -> m b 

लेकिन, हम कैसे data Foo a = F Bool a निश्चित रूप से की तरह एक प्रकार के लिए बाँध को परिभाषित करेगा, हम एक दूसरे के साथ एक फू के एक जोड़ सकता है, लेकिन हम bools कैसे गठबंधन होगा। कल्पना कीजिए कि बूल ने चिह्नित किया है, कहें कि, अन्य पैरामीटर का मूल्य बदल गया है या नहीं। अगर मेरे पास a = Foo False whatever है और मैं इसे एक फ़ंक्शन में बांधता हूं, तो मुझे नहीं पता कि यह फ़ंक्शन whatever को बदलने जा रहा है या नहीं। मैं एक बांध नहीं लिख सकता जो सही ढंग से बूल सेट करता है। इसे अक्सर स्थिर मेटा-सूचना की समस्या कहा जाता है। मैं यह निर्धारित करने के लिए फ़ंक्शन में बाध्य होने का निरीक्षण नहीं कर सकता कि यह whatever बदल जाएगा या नहीं।

इस तरह के कई अन्य मामले हैं: प्रकार जो उत्परिवर्ती कार्यों का प्रतिनिधित्व करते हैं, पार्सर्स जो जल्दी से बाहर निकल सकते हैं, लेकिन बुनियादी विचार यह है: मोनैड एक उच्च बार सेट करते हैं जो सभी प्रकार स्पष्ट नहीं हो सकते हैं। तीर आपको बाइंड को संतुष्ट किए बिना शक्तिशाली तरीकों से प्रकार (जो इस उच्च, बाध्यकारी मानक का समर्थन करने में सक्षम हो सकता है या नहीं कर सकता) लिखने की अनुमति देता है। बेशक, आप monads की कुछ शक्ति खो देते हैं।

कहानी का नैतिक: ऐसा कोई भी तीर नहीं कर सकता है जो मोनड नहीं कर सकता, क्योंकि एक मोनड हमेशा तीर में बनाया जा सकता है। हालांकि, कभी-कभी आप अपने प्रकारों को मोनैड में नहीं बना सकते हैं, लेकिन आप अभी भी उन्हें मोनैड की रचनात्मक लचीलापन और शक्ति रखने की अनुमति देना चाहते हैं।

इन विचारों में से कई शानदार Understanding Haskell Arrows

+2

_ "ऐसा कोई भी तीर नहीं कर सकता है जो मोनड नहीं कर सकता है, क्योंकि एक मोनड हमेशा तीर में बनाया जा सकता है। हालांकि, कभी-कभी आप अपने प्रकार को मोनैड में नहीं बना सकते हैं लेकिन आप अभी भी उन्हें अधिकतर रचनात्मक लचीलापन प्राप्त करने की अनुमति देना चाहते हैं और monads की शक्ति। "_ - वे दो वाक्य पारस्परिक रूप से अनन्य नहीं हैं? पहला व्यक्ति सीधे आपके द्वारा लिंक किए गए पृष्ठ (http://ertes.de/new/tutorials/arrows.html) के पहले पैराग्राफ का भी विरोध करता है, जहां निम्न कहा गया है: "तीर इंटरफ़ेस अधिक सामान्य है, इस प्रकार अनुमति देता है कुछ नियंत्रण संरचनाएं, जिन्हें मोनैडिक ढांचे में व्यक्त नहीं किया जा सकता है। " –

+2

आह यह एक दिलचस्प अर्थपूर्ण मुद्दा है। शायद तीरों और monads के आसपास भ्रम के स्रोतों में से एक। मुझे स्पष्टीकरण देने की कोशिश करें। ऐसा कुछ भी नहीं है जो एक तीर कर सकता है कि एक मोनड नहीं कर सकता, जैसा कि यह एक तीर है। हालांकि, जैसा कि यह किसी चीज का प्रकार है जिसे एक मोनड में नहीं बनाया जा सकता है, यह उन चीजों का समर्थन कर सकता है जो एक मोनड नहीं कर सकते हैं। यह अभी भी भ्रमित लगता है लेकिन मैं इसे कहने का एक बेहतर तरीका नहीं समझ सकता। तीर * उन उदाहरणों को अनुमति देता है जो मोनैड नहीं करते हैं, लेकिन "मोनैड चीज नहीं कर सकती" एक तीर-ची चीज नहीं है, यह एक मोनड होने से तीर उदाहरण को अयोग्य घोषित करता है। –

+1

@ एरिकहिंटन: मैं इसे जोड़ूंगा: निष्पादित होने पर एक कार्रवाई भी हो सकती है और कार्रवाई के दौरान कोई कार्रवाई क्या कर सकती है और इसे निष्पादित करने के अलावा कार्रवाई के उपयोगकर्ता क्या कर सकते हैं। AFAIK, एक 'तीर' पार्सर कुछ भी पार्स नहीं कर सकता है जो 'मोनाद' पार्सर पार्स नहीं कर सकता है, इसलिए उस अर्थ में 'तीर' को निष्पादित करने से 'मोनाद' की तुलना में और कुछ नहीं किया जा सकता है; लेकिन आप 'एरो' पार्सर लिख सकते हैं जिसे इसे चलाने से पहले अपने कई रनटाइम व्यवहार की भविष्यवाणी करने के लिए बाहरी रूप से विश्लेषण किया जा सकता है, जिस तरह से 'मोनाड' इंटरफेस का समर्थन नहीं हो सकता है। इसके उदाहरण के लिए मेरा उत्तर देखें, लेकिन 'रीडर' 'लागूकर्ता 'पर लागू किया गया है। –

1

सवाल बिल्कुल सही नहीं है। यह पूछने की तरह है कि आप सेब के बजाय संतरे क्यों खाएंगे, क्योंकि सेब सभी चारों ओर अधिक पौष्टिक लगते हैं।

मोनैड की तरह तीर, गणनाओं को व्यक्त करने का एक तरीका हैं, लेकिन उन्हें laws के एक अलग सेट का पालन करना होगा। विशेष रूप से, जब आपके पास कार्य-जैसी चीजें होती हैं तो कानूनों का उपयोग करने के लिए तीर अच्छे होते हैं।

हास्केल विकी तीर के लिए few introductions सूचीबद्ध करता है। विशेष रूप से, Wikibook एक अच्छा उच्च स्तरीय परिचय है, और जॉन ह्यूजेस द्वारा tutorial विभिन्न प्रकार के तीरों का एक अच्छा अवलोकन है।

एक वास्तविक दुनिया उदाहरण के लिए, this tutorial जो Hakyll 3 के तीर आधारित इंटरफेस का उपयोग करता है, मोटे तौर पर the same thing Hakyll 4 की इकाई आधारित इंटरफेस के साथ तुलना करें।

12

हर इकाई एक तीर

newtype Kleisli m a b = Kleisli (a -> m b) 
instance Monad m => Category (Kleisli m) where 
    id = Kleisli return 
    (Kleisli f) . (Kleisli g) = Kleisli (\x -> (g x) >>= f) 
instance Monad m => Arrow (Kleisli m) where 
    arr f = Kleisli (return . f) 
    first (Kleisli f) = Kleisli (\(a,b) -> (f a) >>= \fa -> return (fa,b)) 

को जन्म देता है लेकिन, तीर जो monads नहीं हैं। इस प्रकार, तीर हैं जो चीजें करते हैं जिन्हें आप मोनाड्स के साथ नहीं कर सकते हैं। एक अच्छा उदाहरण कुछ स्थिर जानकारी

data StaticT m c a b = StaticT m (c a b) 
instance (Category c, Monoid m) => Category (StaticT m c) where 
    id = StaticT mempty id 
    (StaticT m1 f) . (StaticT m2 g) = StaticT (m1 <> m2) (f . g) 
instance (Arrow c, Monoid m) => Arrow (StaticT m c) where 
    arr f = StaticT mempty (arr f) 
    first (StaticT m f) = StaticT m (first f) 

इस तीर tranformer उपयोगी है क्योंकि यह एक कार्यक्रम के स्थिर गुणों का ट्रैक रखने के लिए किया जा सकता जोड़ने के लिए तीर ट्रांसफार्मर है। उदाहरण के लिए, आप इसका उपयोग करके अपने एपीआई को मापने के लिए इसका उपयोग कर सकते हैं कि आप कितनी कॉल कर रहे हैं।

+3

इसके विपरीत, 'एरोएप्ली' [आपको एक मोनड देता है] (http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Arrow.html#t:ArrowMonad)। – hammar

+1

क्या आप अपने 'स्टेटिकटी' उदाहरण पर विस्तृत कर सकते हैं? मुझे समझ में नहीं आता कि यह 'राज्य' या 'लेखक' मोनड के साथ क्यों नहीं किया जा सका। –

3

खैर से प्रेरित थे, मैं Arrow से Applicative के सवाल बदलकर थोड़ा यहाँ धोखा देने के लिए जा रहा हूँ। बहुत सारे उद्देश्य लागू होते हैं, और मैं तीर से बेहतर आवेदकों को जानता हूं। बस हर Monad की तरह (और वास्तव में, every Arrow is also an Applicative लेकिन not vice-versa, तो मैं बस इसे नीचे थोड़ा आगे Functor के ढलान के नीचे ले रहा हूँ।)

हर Monad भी एक Applicative है एक Arrow है। Applicatives हैं जो Monad s (उदा।, ZipList) नहीं हैं, इसलिए यह एक संभावित उत्तर है।

लेकिन मान लें कि हम एक ऐसे प्रकार से निपट रहे हैं जो Monad उदाहरण के साथ-साथ Applicative स्वीकार करता है। हम कभी भी Monad के बजाय Applicative उदाहरण का उपयोग क्यों कर सकते हैं? कि लाभ के साथ आता है क्योंकि Applicative कम शक्तिशाली है, और:

  1. कुछ चीजें हम जानते हैं कि कि Monad कर सकते हैं जो Applicative नहीं कर सकते हैं। उदाहरण के लिए, यदि हम IO का उदाहरण सरल से एक यौगिक कार्रवाई को इकट्ठा करने के लिए करते हैं, तो हमारे द्वारा लिखे गए कार्यों में से कोई भी किसी अन्य के परिणामों का उपयोग नहीं कर सकता है। सभी आवेदक IO घटक क्रियाओं को निष्पादित कर सकते हैं और अपने परिणामों को शुद्ध कार्यों के साथ जोड़ सकते हैं।
  2. Applicative प्रकार लिखे जा सकते हैं ताकि हम उन्हें निष्पादित करने से पहले कार्यों के शक्तिशाली स्थिर विश्लेषण कर सकें। तो अगर आप एक प्रोग्राम है जो एक Applicative कार्रवाई यह निष्पादित करने से पहले का निरीक्षण लिख सकते हैं, का पता लगा लेता क्या यह करने के लिए जा रहा है, और प्रदर्शन में सुधार करने के लिए कि, बता उपयोगकर्ता क्या किया जाना हो रहा है, आदि

एक उदाहरण के रूप का उपयोग करता है सबसे पहले, मैं Applicative एस का उपयोग कर OLAP गणना भाषा के प्रकार पर डिजाइन करने पर काम कर रहा हूं। प्रकार Monad उदाहरण के लिए स्वीकार करता है, लेकिन मैंने जानबूझकर इसे टालने से बचाया है, क्योंकि मैं कमMonad से अधिक शक्तिशाली पूछताछ करना चाहता हूं। Applicative का अर्थ है कि प्रत्येक गणना एक अनुमानित संख्या में प्रश्नों के नीचे आ जाएगी।

बाद के उदाहरण के रूप में, मैं my still-under-development operational Applicative library से खिलौना उदाहरण का उपयोग करूंगा।

{-# LANGUAGE GADTs, RankNTypes, ScopedTypeVariables #-} 

import Control.Applicative.Operational 

-- | A 'Reader' is an 'Applicative' program that uses the 'ReaderI' 
-- instruction set. 
type Reader r a = ProgramAp (ReaderI r) a 

-- | The only 'Reader' instruction is 'Ask', which requires both the 
-- environment and result type to be @[email protected] 
data ReaderI r a where 
    Ask :: ReaderI r r 

ask :: Reader r r 
ask = singleton Ask 

-- | We run a 'Reader' by translating each instruction in the instruction set 
-- into an @r -> [email protected] function. In the case of 'Ask' the translation is 'id'. 
runReader :: forall r a. Reader r a -> r -> a 
runReader = interpretAp evalI 
    where evalI :: forall x. ReaderI r x -> r -> x 
      evalI Ask = id 

-- | Count how many times a 'Reader' uses the 'Ask' instruction. The 'viewAp' 
-- function translates a 'ProgramAp' into a syntax tree that we can inspect. 
countAsk :: forall r a. Reader r a -> Int 
countAsk = count . viewAp 
    where count :: forall x. ProgramViewAp (ReaderI r) x -> Int 
      -- Pure :: a -> ProgamViewAp instruction a 
      count (Pure _) = 0 
      -- (:<**>) :: instruction a 
      --   -> ProgramViewAp instruction (a -> b) 
      --   -> ProgramViewAp instruction b 
      count (Ask :<**> k) = succ (count k) 

के रूप में सबसे अच्छा के रूप में मैं समझता हूँ, तुम नहीं countAsk लिख सकते हैं: आप के बजाय एक संचालन Applicative कार्यक्रम के रूप में Reader इकाई बारे में हैं, तो आप गिनती करने के लिए कितनी बार वे ask आपरेशन का उपयोग जिसके परिणामस्वरूप Reader रों जांच कर सकते हैं यदि आप एक मोनड के रूप में Reader लागू करते हैं। (मेरी समझ from asking right here in Stack Overflow आता है, मैं जोड़ूंगा।)

यह वही उद्देश्य वास्तव में Arrow के पीछे विचारों में से एक है।Arrow के लिए बड़े प्रेरक उदाहरणों में से एक पार्सर संयोजक डिज़ाइन था जो मोनैडिक पार्सर्स से बेहतर प्रदर्शन प्राप्त करने के लिए "स्थिर जानकारी" का उपयोग करता है। "स्थिर जानकारी" से उनका क्या मतलब है, मेरे Reader उदाहरण के समान ही कम है: Arrow उदाहरण लिखना संभव है जहां पार्सर्स का निरीक्षण मेरे Reader एस की तरह किया जा सकता है। फिर पार्सिंग लाइब्रेरी, एक पार्सर निष्पादित करने से पहले, यह देखने के लिए जांच कर सकती है कि क्या यह भविष्यवाणी कर सकता है कि यह असफल हो जाएगा, और उस स्थिति में इसे छोड़ दें।

आपके प्रश्न पर सीधी टिप्पणियों में से एक में, जेबरीमैन का उल्लेख है कि तीर वास्तव में लोकप्रियता खो रहे हैं। जैसा कि मैंने इसे देखा है, मैं इसे जोड़ूंगा, Applicative क्या तीर लोकप्रियता खो रहे हैं।


संदर्भ:

  • पाओलो Capriotti & Ambrus कापोसी, "Free Applicative Functors"। बहुत अत्यधिक अनुशंसित।
  • Gergo Erdi, "Static analysis with Applicatives"। प्रेरक, लेकिन मुझे इसका पालन करना मुश्किल है ...
+0

मैं हूं सुनिश्चित नहीं है कि क्या दावा है कि अधिकारी तीर नहीं बनाते हैं। आवेदक तीर ट्रांसफार्मर 'न्यूटाइप ऐपएरो एफ सी ए बी = एपएरो (एफ (सी ए बी)) का नेतृत्व करते हैं। और, परिणामस्वरूप तीर के लिए आवेदक मज़ेदार से मूल्यों को इंजेक्ट करना संभव होना चाहिए। –

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