2009-07-21 10 views
5

मान लीजिए मैं कुछ डेटा जिसका प्रकार है कि एक हास्केल कार्यक्रम में है कुछ की तरह:डेटा संरचनाओं में घोंसला, monads के अंदर मूल्य?

  • IO [ IO (Int, String, Int) ], या
  • IO [ (Int, String, IO Int) ], या
  • [ (Int, String, IO Int) ]

लेकिन मैं शुद्ध कार्यों कि होना चाहिए है [ (Int, String, Int) ] पर काम करें। ऐसा लगता है मैं, जब तक मैं आईओ [(इंट, स्ट्रिंग, इंट)] की तरह कुछ मिल गया और उसके बाद (आईओ इकाई के अंदर से) अनाड़ीपन से आईओ इकाई से अंदर मूल्यों हटाने के लिए शुद्ध कार्यों लागू चाहते हैं। ऐसा करने के लिए कोई आसान पूर्व परिभाषित तरीका नहीं है, मुझे लगता है? कुछ है कि एक इकाई में एक पूरे डेटा संरचना उठा होगा, शुद्ध प्रकार में सभी अंदर प्रकार मोड़? (यह बहुत सुविधाजनक होगा!)

+1

उत्कृष्ट उत्तरों के लिए धन्यवाद दोस्तों! आप बिल्कुल सहायक थे! – Jay

उत्तर

6

आप Control.Monad मॉड्यूल, या applicatives के लिए liftA* कार्यों से liftM* समारोह इस्तेमाल कर सकते हैं।

liftM आप एक इकाई के अंदर काम करने के लिए एक शुद्ध समारोह, उदा .:

ghci> let s = return "Hello" :: IO String 
ghci> liftM reverse s 
"olleH" 

इस तरह आप मैन्युअल रूप से की तरह "s >>= \x -> return (reverse x)" हर जगह बातें लिखने के लिए नहीं है लिफ्ट करने के लिए अनुमति देता है।

हालांकि, यह आपके [(String, Int, IO Int)] उदाहरण के साथ मदद नहीं करेगा, शुद्ध समारोह आप एक [(String, Int, Int)] के साथ सौदा किया है। चूंकि टुपल में तीसरा तत्व वास्तव में Int नहीं है।

उस स्थिति में मैं पहले एक समारोह [(String, Int, IO Int)] -> IO [(String, Int, Int)] लिखने का सुझाव देता हूं और यह उठाया शुद्ध कार्य लागू करता है।


यह सबसे सामान्य समारोह मैं यह करने के साथ आने कर सकता है:

conv :: Monad m => (f (m a) -> m (f a)) -> [f (m a)] -> m [f a] 
conv f = sequence . map f 

आप यह इतना की तरह कॉल कर सकते हैं:

liftTrd :: Monad m => (a, b, m c) -> m (a, b, c) 
liftTrd (x, y, mz) = mz >>= \z -> return (x, y, z) 

conv liftTrd [("hi", 4, return 2)] :: IO [(String, Int, Int)] 

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

+0

यह दिलचस्प है! हो सकता है कि भाषा इस में निर्मित की तरह कुछ होना चाहिए?ऐसा कुछ जो सभी प्रकार के लिए काम करेगा (एक सूची * के अंदर * एक टुपल के बारे में सोचें, उदाहरण के लिए - या बीजगणित फाटा प्रकार ...) – Jay

+0

वैसे ... अनुक्रम का उपयोग करने का अर्थ यह होगा कि मैं इसे अनंत सूचियों पर उपयोग नहीं कर सकता , सही? – Jay

+0

@Jay: शायद 'unsafeInterleaveIO' के साथ कुछ किया जा सकता है, लेकिन असीमित सूची में वास्तव में' अनुक्रम 'काफी लंबा समय लगता है। – ephemient

4

पहले नीचे समाधान के लिए कुछ के उपयोग का उदाहरण reduce कहा जाता है (जब तक आप एक बेहतर नाम का सुझाव):

> reduce [(["ab", "c"], "12")] :: [(String, String)] 
[("ab","12"),("c","12")] 

> reduce [(["ab", "c"], "12")] :: [(Char, Char)] 
[('a','1'),('a','2'),('b','1'),('b','2'),('c','1'),('c','2')] 

> reduce [("ab", "12"), ("cd", "3")] :: [(Char, Char)] 
[('a','1'),('a','2'),('b','1'),('b','2'),('c','3'),('d','3')] 

आपका उदाहरण भी इसके साथ हल किया जाता है:

complexReduce :: Monad m => m (m (a, b, m [m (c, m d)])) -> m (a, b, [(c, d)]) 
complexReduce = reduce 

और reduce के कार्यान्वयन :

{-# LANGUAGE FlexibleContexts, FlexibleInstances, IncoherentInstances, MultiParamTypeClasses, UndecidableInstances #-} 

import Control.Monad 

-- reduce reduces types to simpler types, 
-- when the reduction is in one of the following forms: 
-- * make a Monad disappear, like join 
-- * move a Monad out, like sequence 
-- the whole magic of Reduce is all in its instances 
class Reduce s d where 
    reduce :: s -> d 

-- Box is used only for DRY in Reduce instance definitions. 
-- Without it we, a Reduce instance would need 
-- to be tripled for each variable: 
-- Once for a pure value, once for a monadic value, 
-- and once for a reducable value 
newtype Box a = Box { runBox :: a } 
instance Monad m => Reduce (Box a) (m a) where 
    reduce = return . runBox 
instance Reduce a b => Reduce (Box a) b where 
    reduce = reduce . runBox 
redBox :: Reduce (Box a) b => a -> b 
redBox = reduce . Box 

-- we can join 
instance (Monad m 
    , Reduce (Box a) (m b) 
) => Reduce (m a) (m b) where 
    reduce = join . liftM redBox 

-- we can sequence 
-- * instance isnt "Reduce [a] (m [b])" so type is always reduced, 
-- and thus we avoid overlapping instances. 
-- * we cant make it general for any Traversable because then 
-- the type system wont find the right patterns. 
instance (Monad m 
    , Reduce (Box a) (m b) 
) => Reduce (m [a]) (m [b]) where 
    reduce = join . liftM (sequence . fmap redBox) 

instance (Monad m 
    , Reduce (Box a) (m c) 
    , Reduce (Box b) (m d) 
) => Reduce (a, b) (m (c, d)) where 
    reduce (a, b) = liftM2 (,) (redBox a) (redBox b) 

instance (Monad m 
    , Reduce (Box a) (m d) 
    , Reduce (Box b) (m e) 
    , Reduce (Box c) (m f) 
) => Reduce (a, b, c) (m (d, e, f)) where 
    reduce (a, b, c) = 
    liftM3 (,,) (redBox a) (redBox b) (redBox c)