2013-11-23 9 views
7

हास्केल Control.Arrow प्रलेखन में यह मोनैड के क्लेस्ली तीर के संबंधों के बारे में बात करता है, लेकिन यह मेरे लिए स्पष्ट नहीं है कि इसका उपयोग कैसे किया जाए। मेरे पास एक ऐसा कार्य है जो मुझे लगता है कि आईओ मोनैड को छोड़कर तीरों के साथ फिट बैठता है, इसलिए मुझे लगता है कि क्लेस्ली तीर मदद कर सकते हैं।मोनैड के साथ क्लेस्ली तीरों का उपयोग कैसे करें?

निम्न कार्य करें जो निर्देशिका के मूल और संशोधित फ़ाइल नामों के जोड़े देता है।

import System.Directory 
import System.FilePath 

datedFiles target = do 
    fns <- getDirectoryContents target 
    tms <- mapM (fmap show . getModificationTime) fns 
    return $ 
     zip fns $ 
     zipWith replaceBaseName fns $ 
     zipWith (++) (map takeBaseName fns) tms 

अगर मैं इसे बाहर आकर्षित करने के लिए किया था, यह कुछ इस तरह होगा:

enter image description here

मुझे लगता है कि यह Kleisli तीर के प्रयोग से लाभ कर सकते हैं, लेकिन मैं पता नहीं कैसे । क्या कोई मार्गदर्शन प्रदान कर सकता है?

उत्तर

6

datedFiles के बराबर अपने चित्र के रूप में पता चलता है, क्योंकि जानकारी एक "तय पाइपलाइन" में बहती है तीर का उपयोग करके लागू किया जा सकता है।

import System.Directory 
import System.FilePath 
import Control.Monad.List 
import Control.Arrow 

datedFiles :: FilePath -> IO [(FilePath,FilePath)] 
datedFiles = fmap runListT . runKleisli $ 
    (Kleisli $ ListT . getDirectoryContents) 
    >>> 
    returnA &&& ((Kleisli $ liftIO . getModificationTime) >>^ show) 
    >>^ 
    fst &&& (\(path,time) -> replaceBaseName path $ takeBaseName path ++ time) 

बेशक, यह सबसे सहज कार्यान्वयन नहीं है:

यहाँ एक संभव कार्यान्वयन कि सूची में map या zip का उपयोग नहीं करता है।

क्लेस्ली तीर के लिए मोनड ListT IO है, हालांकि एकमात्र नोडेटर्मिनिज्म getDirectoryContents के कारण होता है।

ध्यान दें कि अंतिम पंक्ति एक शुद्ध कार्य है; अंतिम पंक्ति के लिए (&&&) कार्यों के लिए तीर उदाहरण का उपयोग कर रहा है।

संपादित करें: lens पैकेज सेWrapped typeclass में थोड़ा और अधिक संक्षेप में जोड़ने के लिए/newtype रैपर को दूर किया जा सकता है। पिछले उदाहरण के लिए इसे लागू करने के लिए, हम साथ अंत:

import Control.Lens 

datedFiles :: FilePath -> IO [(FilePath,FilePath)] 
datedFiles = fmap runListT . runKleisli $ 
    ListT . getDirectoryContents ^. wrapped 
    >>> 
    returnA &&& (liftIO . getModificationTime ^. wrapped >>^ show) 
    >>^ 
    fst &&& (\(path,time) -> replaceBaseName path $ takeBaseName path ++ time) 
7

मोनाड्स हास्क से Functor एस हैस्क, प्रकार के कार्यों और कार्यों की श्रेणी, हास्क --- एक एंडोफंक्टर। इसका मतलब है कि कुछ Monadm के लिए हास्क में कुछ तीर a -> m b जैसा दिखते हैं। एक विशेष मोनड m के लिए, हास्क की उपश्रेणी जहां तीर a -> m b की तरह दिखते हैं m के लिए क्लेस्ली श्रेणी है।

हम जानते हैं कि यह एक वर्ग है एक पहचान है क्योंकि तीर return :: a -> m a और संरचना (>>>) :: (a -> m b) -> (b -> m c) -> (a -> m c)

(f >>> g) a = join (g <$> f a) 

तरह परिभाषित किया गया है यही वजह है कि हम इस एक Monad होने की जरूरत है --- हम return और join दोनों का उपयोग करें।


हास्केल में, हम सिर्फ सामान्य रूप से एक उपश्रेणी नहीं हो सकता है, लेकिन बजाय एक newtype प्रयोग किया जाता है।

import Prelude hiding ((.), id) 
import Control.Category 

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b } 

instance Monad m => Category (Kleisli m) where 
    id     = Kleisli return 
    Kleisli g . Kleisli f = Kleisli (join . fmap g . f) 

और फिर हम एक वर्ग में Kleisli m a b रों, तीर के प्रकार Monad m => a -> m b के कार्यों उन्नयन कर सकते हैं, और, (.)

arr :: Kleisli IO FilePath [String] 
arr = Kleisli (mapM $ fmap show . getModificationTime) . Kleisli getDirectoryContents 

आम तौर पर है कि एक सा वाक्य रचना शोर है के साथ उन्हें रचना यद्यपि। और (.) ओवरलोड करने के लिए Category टाइपक्लास का उपयोग करने के लिए नया प्रकार केवल मूल्यवान है। इसके बजाय यह है कि आप देखेंगे अधिक होने की संभावना है return और (>=>) जो

return a = runKleisli (id a) 
f >=> g = runKleisli $ Kleisli g . Kleisli f 
2

सबसे पहले मैं इस सूची से निपटने से किसी एक फ़ाइल से निपटने विभाजित करने के लिए आप सुझाव देना चाहेंगे। आपके उदाहरण में, timestamp दिलचस्प तीर है, क्योंकि अन्य सभी शुद्ध कार्य हैं। फिर भी, हम उदाहरण को और अधिक रोचक बनाने के लिए उनमें से कुछ को तीरों में बना सकते हैं। arrow notation का उपयोग करते हुए हम एक Kleisli तीर के रूप में एक फ़ाइल नाम कंप्यूटिंग पुनर्लेखन कर सकते हैं:

{-# LANGUAGE Arrows #-} 
import Control.Arrow 
import System.Directory 
import System.FilePath 
import System.Time 

-- Get a timestamp of a file as an arrow: 
timestamp :: Kleisli IO FilePath ClockTime 
timestamp = Kleisli getModificationTime 

-- Insert a given string in front of the extension of a file. 
-- Just as an example - we'd rather use a simple `let` instead of making it 
-- an arrow. 
append :: (Monad m) => Kleisli m (FilePath, String) FilePath 
append = arr $ \(fn, suffix) -> 
       let (base, ext) = splitExtension fn 
       in base ++ suffix ++ ext 

-- Given a directory, receive the name of a file as an arrow input 
-- and produce the new file name. (We could also receive `dir` 
-- as an input, if we wanted.) 
datedArrow :: FilePath -> Kleisli IO FilePath (FilePath, FilePath) 
datedArrow dir = proc fn -> do 
        ts <- timestamp -< replaceDirectory fn dir 
        fn' <- append -< (fn, show ts) 
        returnA -< (fn, fn') 

datedFiles' :: FilePath -> IO [(FilePath, FilePath)] 
datedFiles' target = do 
       fns <- getDirectoryContents target 
       mapM (runKleisli $ datedArrow target) fns 
1

Let इकाई से मुख्य कार्य याद रखें:

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

और अब के Kleisli

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b } 

जहां को देखो Kleisli एक रैपर और runKleisli है - नए प्रकार से अनचाहे।

आम बात क्या है? a -> m b हिस्सा

और के उदाहरण घोषणा को देखते हैं:

instance Monad m => Arrow (Kleisli m) where ... 

हम देखते हैं, कैसे Arrow

की Monad हिस्सा बनाने के लिए
संबंधित मुद्दे