2012-08-26 11 views
17

सवाल Tacit function composition in Haskell, लोगों a -> r के लिए एक Num उदाहरण बनाने उल्लेख की टिप्पणी में, इसलिए मैंने सोचा कि मैं समारोह अंकन का उपयोग गुणन का प्रतिनिधित्व करने के साथ खेलते हैं चाहते हैं:नंबर (अजीब लेकिन मनोरंजक)

{-# LANGUAGE TypeFamilies #-} 
import Control.Applicative 

instance Show (a->r) where -- not needed in recent GHC versions 
    show f = " a function " 

instance Eq (a->r) where  -- not needed in recent GHC versions 
    f == g = error "sorry, Haskell, I lied, I can't really compare functions for equality" 

instance (Num r,a~r) => Num (a -> r) where 
    (+) = liftA2 (+) 
    (-) = liftA2 (-) 
    (*) = liftA2 (*) 
    abs = liftA abs 
    negate = liftA negate 
    signum = liftA signum 
    fromInteger a = (fromInteger a *) 

ध्यान दें कि सेइंटर परिभाषा का अर्थ है कि मैं 3 4 लिख सकता हूं जो 12, और 7 (2+8) का मूल्यांकन करता है, जैसा कि आप उम्मीद करेंगे।

तो यह सब अद्भुत, मनोरंजक रूप से अजीब हो जाता है! इस wierdness कृपया विस्तार से बताएं यदि आप कर सकते हैं:

*Main> 1 2 3 
18 
*Main> 1 2 4 
32 
*Main> 1 2 5 
50 
*Main> 2 2 3 
36 
*Main> 2 2 4 
64 
*Main> 2 2 5 
100 
*Main> (2 3) (5 2) 
600 

[संपादित करें:। इस्तेमाल किया इकाई के बजाय अनुप्रयोगी क्योंकि अनुप्रयोगी आम तौर पर बहुत अच्छा है, लेकिन यह कोड को बिल्कुल भी ज्यादा फर्क नहीं है]

+2

जीएचसी 7.4 में, डमी 'शो' और' ईक 'उदाहरणों को हटाना संभव है, क्योंकि' अब 'अब उन्हें आवश्यकता नहीं है। – sdcvvc

+3

'मोनाद' यहां अधिक है। सरल और अधिक सामान्य 'आवेदक' पर्याप्तताएं। – Conal

+0

@ एसडीसीवीवीसी मैं जल्द ही कुछ ही उन्नयन कर रहा हूं, हां। – AndrewC

उत्तर

21

एक में आपके उदाहरणों के साथ 2 3 4 जैसी अभिव्यक्ति, 2 और 3 दोनों कार्य हैं। तो 2 वास्तव में (2 *) है और इसमें Num a => a -> a है। वही है। 2 3 तब (2 *) (3 *) है जो 2 * (3 *) जैसा ही है। आपके उदाहरण से, यह liftM2 (*) 2 (3 *) है जो liftM2 (*) (2 *) (3 *) है। अब यह अभिव्यक्ति आपके किसी भी उदाहरण के बिना काम करती है।

तो इसका क्या अर्थ है? ठीक है, liftM2 कार्यों के लिए एक डबल संरचना है। विशेष रूप से, liftM2 f g h\ x -> f (g x) (h x) जैसा ही है। तो liftM2 (*) (2 *) (3 *) तब \ x -> (*) ((2 *) x) ((3 *) x) है। थोड़ा सा सरलीकरण, हमें मिलता है: \ x -> (2 * x) * (3 * x)। तो अब हम जानते हैं कि 2 3 4 वास्तव में (2 * 4) * (3 * 4) है।

अब, liftM2 फ़ंक्शन के लिए इस तरह से काम क्यों करता है? के (->) r के लिए इकाई उदाहरण को देखो (ध्यान रखें कि (->) r(r ->) है, लेकिन हम प्रकार-स्तरीय ऑपरेटर वर्गों नहीं लिख सकते हैं) करते हैं:

instance Monad ((->) r) where 
    return x = \_ -> x 
    h >>= f = \w -> f (h w) w 

तो returnconst है। >>= थोड़ा अजीब है। मुझे लगता है कि इसे join के संदर्भ में देखना आसान है। कार्यों के लिए, join इस तरह काम करता है:

join f = \ x -> f x x 

है तो उसे दो तर्कों के एक समारोह लेता है और एक तर्क के एक समारोह में उस कथन की दो बार का उपयोग करके इसे बदल जाता है। काफी सरल। यह परिभाषा भी समझ में आता है। कार्यों के लिए, join को दो तर्कों का एक फ़ंक्शन एक फ़ंक्शन में बदलना है; ऐसा करने का एकमात्र उचित तरीका उस तर्क को दो बार उपयोग करना है।

>>=fmapjoin के बाद है। कार्यों के लिए, fmap सिर्फ (.) है।तो अब >>= के बराबर है:

h >>= f = join (f . h) 

जो सिर्फ है:

h >>= f = \ x -> (f . h) x x 

अब हम सिर्फ पाने के लिए . से छुटकारा पाने:

h >>= f = \ x -> f (h x) x 

तो अब है कि हम जानते हैं कि कैसे >>= काम करता है , हम liftM2 देख सकते हैं। इस प्रकार liftM2 परिभाषित किया गया है: थोड़ा करके

liftM2 f a b = a >>= \ a' -> b >>= \ b' -> return (f a' b') 

हम तो बस इस बिट कर सकते हैं। सबसे पहले, return (f a' b')\ _ -> f a' b' में बदल जाता है। \ b' -> के साथ संयुक्त, हम पाते हैं: \ b' _ -> f a' b'। दूसरा x के बाद से

\ x -> (\ b' _ -> f a' b') (b x) x 

नजरअंदाज कर दिया है, हम पाते हैं:: फिर b >>= \ b' _ -> f a' b' में बदल जाता है \ x -> (\ b' -> f a' b') (b x) जो तब \ x -> f a' (b x) करने के लिए कम है। तो यह हमारे साथ छोड़ देता है:

a >>= \ a' -> \ x -> f a' (b x) 

फिर, हम स्थानापन्न >>=:

\ y -> (\ a' x -> f a' (b x)) (a y) y 

इस कम कर देता है के लिए:

\ y -> f (a y) (b y) 

जो वास्तव में है क्या हम पहले liftM2 रूप में इस्तेमाल किया!

उम्मीद है कि अब 2 3 4 का व्यवहार पूरी तरह से समझ में आता है।

+0

आह हाँ - जैसे ही आप 'liftM2 (*) (2 *) (3 *) 4' पर पहुंचे, मैंने देखा कि यह आखिरी तर्क क्यों था - इसका मतलब है' (+) $ (2 *) 4 $ (3 *) 4'। और '(2 3) (5 2) 'में अनावश्यक ब्रैकेट हैं, इसलिए यह केवल 2 2 (5 2)' है और इसी कारण से 300 है। – AndrewC

+0

वैसे, मैं '(->) आर' के लिए आवेदक और मोनाड उदाहरणों से खुश हूं और इस सवाल में ऐसा कहा जाना चाहिए था, यह सिर्फ मेरे दिमाग में मेरे कानों से बाहर निकलना शुरू कर दिया जब मैंने '2 3 4 किया ', और मैंने हाथ-मूल्यांकन करने की कोशिश भी नहीं की। doh! आपकी स्पष्टीकरण दूसरों के लिए इसे कहीं अधिक स्पष्ट कर देगी, इसलिए धन्यवाद। – AndrewC

+2

@ एंड्रयूसी: मैंने अपनी विचार प्रक्रिया को तब लिखा था जब मैं यह पता लगा रहा था कि वास्तव में क्या '2 3 4' किया गया था, इसलिए यह स्वयं को किसी और की मदद कर रहा था: पी। –