2013-02-27 26 views
5

मैं मुसीबत समझ कैसे इस हास्केल अभिव्यक्ति काम करता आ रही हैं:कृपया व्याख्या (forM_ [stdout, stderr] फ्लिप hPutStrLn।) :: स्ट्रिंग -> आईओ()

import Control.Monad 
import System.IO 
(forM_ [stdout, stderr] . flip hPutStrLn) "hello world" 

. flip hPutStrLn हिस्सा वास्तव में क्या कर रहा है ? प्रकार हस्ताक्षर जटिल लगते:

ghci> :type flip 
flip :: (a -> b -> c) -> b -> a -> c 
ghci> :type (.) 
(.) :: (b -> c) -> (a -> b) -> a -> c 
ghci> :type (. flip) 
(. flip) :: ((b -> a -> c1) -> c) -> (a -> b -> c1) -> c 
ghci> :type (. flip hPutStrLn) 
(. flip hPutStrLn) :: ((Handle -> IO()) -> c) -> String -> c 

क्या हो जाता है अभिव्यक्ति के रूप में (.) ऑपरेटर के बाएँ और दाएँ ऑपरेंड मूल्यांकन किया जाता है?

मेरे सवाल डाल करने के लिए एक और रास्ता है, कैसे इस तरह एक प्रकार हस्ताक्षर के साथ शीर्ष अंत तक पर अभिव्यक्ति के बाएं हिस्से करता है:

(forM_ [stdout, stderr] . flip hPutStrLn) :: String -> IO() 
+0

': प्रकार (। HPutStrLn)' की तुलना करता है, 'टाइप (। Flip hPutStrLn)' सहायता के साथ? –

+1

ऐसे कोड कौन लिखेंगे? पॉइंट-फ्री शैली वास्तव में यहां व्यर्थ है और पठनीयता को प्रभावित करती है, आईएमएचओ –

+1

@ निकलासबी। यह मेरे लिए बहुत बुरा नहीं है, हालांकि मुझे अतीत में एक समय याद है जब यह पूरी तरह से अपारदर्शी होता। यह शैली के साथ आपकी परिचितता पर निर्भर करता है, मुझे लगता है (या बल्कि, स्पष्ट रूप से)। – luqui

उत्तर

13

(.) के बाएँ और दाएँ ऑपरेंड

forM_ [stdout, stderr] 

और

flip hPutStrLn 

क्रमशः रहे हैं।

hPutStrLn के प्रकार

hPutStrLn :: Handle -> String -> IO() 

है तो flip hPutStrLn टाइप

flip hPutStrLn :: String -> Handle -> IO() 

प्रकार प्रणाली आपको बताता है के रूप में, flip है एक Combinator है कि एक और समारोह के तर्कों के आदेश स्वैप है। सार में निर्दिष्ट

flip  :: (a -> b -> c) -> b -> a -> c 
flip f x y = f y x 

ghci से आप पहले से ही पता है कि (. flip hPutStrLn) के प्रकार

ghci> :type (. flip hPutStrLn) 
(. flip hPutStrLn) :: ((Handle -> IO()) -> c) -> String -> c 

दूसरी दिशा से कार्य करना है, बाईं ओर के प्रकार

ghci> :type forM_ [stdout, stderr] 
forM_ [stdout, stderr] :: Monad m => (Handle -> m b) -> m() 

का निरीक्षण करें है कैसे प्रकार एक साथ फिट बैठते हैं।

(. flip hPutStrLn)  ::   ((Handle -> IO()) -> c ) -> String -> c 
forM_ [stdout, stderr] :: Monad m => (Handle -> m b) -> m() 

का मेल दो (दूसरा के साथ पहली बार फोन कर) देता है

ghci> :type forM_ [stdout, stderr] . flip hPutStrLn 
forM_ [stdout, stderr] . flip hPutStrLn :: String -> IO() 

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

point-free style के साथ आपके प्रश्न में परिभाषा के रूप में, प्रोग्रामर (.) के साथ उन्हें लिखकर छोटे, सरल कार्यों के संदर्भ में अधिक जटिल कार्यों को परिभाषित करता है। flip संयोजक पुनर्गठन तर्कों के लिए उपयोगी है ताकि बार-बार आंशिक अनुप्रयोग एक साथ फिट हो सकें।

+0

के बाईं तरफ 'forM_ [x, y] 'को कभी नहीं देखा है बहुत स्पष्ट उत्तर। धन्यवाद। – dan

+1

@ डैन आपका स्वागत है! इसके साथ बने रहें। हास्केल सीखने की यात्रा अद्भुत है। –

7

flip एक इनपुट समारोह, यानी के तर्कों को उलट देता है:

flip hPutStrLn == \a b -> hPutStrLn b a 

. एक समारोह रचना ऑपरेटर (या इन्फ़िक्स समारोह), तो आप अच्छी तरह से एक साथ देता है जो श्रृंखला कार्यों है। इस ऑपरेटर के बिना अपने अभिव्यक्ति के रूप में निम्नानुसार फिर से लिखा जा सकता है:

forM_ [stdout, stderr] ((flip hPutStrLn) "hello world") 

जो रूप में ही है:

forM_ [stdout, stderr] (flip hPutStrLn "hello world") 

या, आवेदन ऑपरेटर का उपयोग:

forM_ [stdout, stderr] $ flip hPutStrLn "hello world" 

के संबंध में . सवाल का संचालन करता है।

(.) :: (b -> c) -> (a -> b) -> a -> c 

आप 3 तर्कों से एक समारोह के रूप में यह देख सकते हैं: . के प्रकार के हस्ताक्षर करने पर विचार करें एक समारोह b -> c, एक समारोह a -> b और एक मूल्य a - यह भी कारण Currying एक परिणामस्वरूप मूल्य c के लिए, लेकिन, आप इसे दो तर्कों से एक समारोह के रूप में देख सकते हैं: b -> c और a -> b - a -> c प्रकार के परिणामस्वरूप कार्य के लिए। और यह आपके उदाहरण में होता है: आप दो कार्यों को पास करते हैं (forM_ [stdout, stderr] और flip hPutStrLn, जो खुद को करी के परिणाम होते हैं) . पर और परिणामस्वरूप String -> IO() टाइप करें।

+3

जो 'forM_ [stdout, stderr] (\ h -> hPutStrLn h" हैलो वर्ल्ड "के समान है)' – luqui

+0

उपर्युक्त उदाहरण में (।) फ़ंक्शन के बाएं और दाएं संचालन क्या हैं? – dan

+0

@dan अद्यतन –

2

यहां उस प्रकार का कुछ छोटा व्युत्पन्न है (जैसा कि निकिता वोल्कोव के उत्तर के दूसरे भाग में संकेत दिया गया है)।

(.) :: (b -> c) -> (a -> b) -> a -> c और (f . g) x = f (g x), यह जानते हुए कि इतना

(f . g) :: a -> c  where g :: (a -> b) and f :: (b -> c) 

(a -> b में b और b -> cthe unification प्रदर्शन, a -> c प्रकार देने के बाद गायब हो जाता है) कि और के बाद से

flip hPutStrLn   :: String -> (Handle -> IO())    -- g 
forM_ [stdout, stderr] :: (Monad m) => (Handle -> m b) -> m()  -- f 

(हम डाल कोष्ठकों के आसपास Handle -> IO() पहले प्रकार में, इस तथ्य का उपयोग करते हुए कि -> में है सही साहचर्य), पहले (function composition ऑपरेटर के माध्यम से) के साथ दूसरे रचना की जिसके परिणामस्वरूप प्रकार

(Monad m) => String -> m()  where m ~ IO and b ~() 
           (found by unification of 
             Handle -> IO() and 
             Handle -> m b  ) 

अर्थात String -> IO() है।

(.) के लिए तर्कों का क्रम थोड़ा उपयोग करने में आता है; यह पहले अपना दूसरा तर्क फ़ंक्शन निकाल देता है, और फिर इसके पहले तर्क फ़ंक्शन को कॉल करने के लिए परिणाम का उपयोग करता है।अगर हम Control.Arrow आयात करते हैं तो हम >>> ऑपरेटर का उपयोग कर सकते हैं जो (.) की तरह है, कार्यों के साथ: (f . g) x == (g >>> f) x

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