17

मैं एक मोनैड ट्रांसफार्मर की तलाश में हूं जिसका उपयोग किसी प्रक्रिया की प्रगति को ट्रैक करने के लिए किया जा सकता है। मुझे पता है step क्योंकि monadic कानूनों की स्पष्ट रूप से मौजूद नहीं है, और task है कि इस कार्यक्रम के नियतिवाद की वजह से एक स्पष्ट कदम संख्या पैरामीटर के लिए/प्रगति ट्रैकिंग के लिए मोनाड ट्रांसफार्मर

procedure :: ProgressT IO() 
procedure = task "Print some lines" 3 $ do 
    liftIO $ putStrLn "line1" 
    step 
    task "Print a complicated line" 2 $ do 
    liftIO $ putStr "li" 
    step 
    liftIO $ putStrLn "ne2" 
    step 
    liftIO $ putStrLn "line3" 

-- Wraps an action in a task 
task :: Monad m 
    => String  -- Name of task 
    -> Int   -- Number of steps to complete task 
    -> ProgressT m a -- Action performing the task 
    -> ProgressT m a 

-- Marks one step of the current task as completed 
step :: Monad m => ProgressT m() 

: यह कैसे उपयोग किया जाएगा समझाने के लिए, निम्न कोड पर विचार रोकथाम की समस्या।

इकाई के रूप में ऊपर वर्णित है, के रूप में मैं इसे देख, दो तरीकों में से एक में लागू किया जा सकता है: एक समारोह है कि वर्तमान कार्य का नाम/कदम सूचकांक ढेर हो जाएंगे और में एक निरंतरता के माध्यम से

  1. इस बिंदु पर प्रक्रिया कि यह छोड़ दिया। लौटे निरंतरता पर बार-बार इस फ़ंक्शन को कॉल करना प्रक्रिया के निष्पादन को पूरा करेगा।
  2. एक फ़ंक्शन के माध्यम से एक कार्यवाही करने का वर्णन किया गया है कि कार्य कार्य पूरा होने पर क्या करना है। जब तक यह पूरा नहीं किया जाता है, तब तक प्रक्रिया अनियंत्रित रूप से चलती है, प्रदान की गई कार्रवाई के माध्यम से परिवर्तन के बारे में पर्यावरण को "सूचित" करती है।

समाधान के लिए (1), मैंने पर Yield निलंबन फ़ैक्टर के साथ देखा है। समाधान के लिए (2), मुझे किसी भी पहले से उपलब्ध मोनैड ट्रांसफार्मर के बारे में पता नहीं है जो उपयोगी होगा।

जो समाधान मैं ढूंढ रहा हूं उसके पास बहुत अधिक प्रदर्शन ओवरहेड नहीं होना चाहिए और जितना संभव हो सके प्रक्रिया पर अधिक नियंत्रण की अनुमति दें (उदा। आईओ एक्सेस या कुछ की आवश्यकता नहीं है)।

इन समाधानों में से कोई एक ध्वनि व्यवहार्य है, या इस समस्या के लिए कहीं और समाधान हैं? क्या इस समस्या को पहले से ही एक मोनैड ट्रांसफॉर्मर के साथ हल किया गया है जिसे मैं ढूंढने में असमर्थ हूं?

संपादित करें: लक्ष्य यह जांचना नहीं है कि सभी कदम किए गए हैं या नहीं। लक्ष्य यह चल रहा है कि प्रक्रिया को "निगरानी" करने में सक्षम होना चाहिए, ताकि कोई यह बता सके कि इसमें से कितना पूरा हो गया है।

+0

आप निरंतरता का उल्लेख किया ... शायद मुझे कुछ स्पष्ट याद आ रही है लेकिन मुझे आश्चर्य है कि क्या आप सी का उपयोग कर सकते हैं ontinuation monad ट्रांसफॉर्मर 'ContT'। – mergeconflict

+0

जब तक आप 'putStr' और' putStrLn' को 'स्ट्रिंग -> प्रोग्रेसटी आईओ()' प्रकार के साथ अनुपूरित नहीं करते हैं, तो आपको उन्हें उठाना होगा। ऐसा करने के लिए 'liftIO' का उपयोग करें। –

+0

प्रगति जानकारी का निर्माण और प्रदर्शन एक प्रकाशित/सदस्यता प्रणाली है। हुड के तहत इसे कैसे कार्यान्वित किया जाए इस पर निर्भर करेगा कि मुख्य धागा या विशेष अन्य थ्रेड या कई अन्य धागे प्रगति स्थिति पर कार्य करेंगे या नहीं। –

उत्तर

4

यह इस समस्या का मेरा निराशावादी समाधान है। यह प्रत्येक चरण पर गणना को निलंबित करने के लिए Coroutine एस का उपयोग करता है, जो उपयोगकर्ता को कुछ प्रगति की रिपोर्ट करने के लिए मनमाने ढंग से गणना करने देता है।

संपादित करें: इस समाधान का पूरा कार्यान्वयन here पाया जा सकता है।

क्या इस समाधान में सुधार किया जा सकता है?

सबसे पहले, यह कैसे किया जाता है:

-- The procedure that we want to run. 
procedure :: ProgressT IO() 
procedure = task "Print some lines" 3 $ do 
    liftIO $ putStrLn "--> line 1" 
    step 
    task "Print a set of lines" 2 $ do 
    liftIO $ putStrLn "--> line 2.1" 
    step 
    liftIO $ putStrLn "--> line 2.2" 
    step 
    liftIO $ putStrLn "--> line 3" 

main :: IO() 
main = runConsole procedure 

-- A "progress reporter" that simply prints the task stack on each step 
-- Note that the monad used for reporting, and the monad used in the procedure, 
-- can be different. 
runConsole :: ProgressT IO a -> IO a 
runConsole proc = do 
    result <- runProgress proc 
    case result of 
    -- We stopped at a step: 
    Left (cont, stack) -> do 
     print stack  -- Print the stack 
     runConsole cont -- Continue the procedure 
    -- We are done with the computation: 
    Right a -> return a 

उपरोक्त कार्यक्रम आउटपुट:

--> line 1 
[Print some lines (1/3)] 
--> line 2.1 
[Print a set of lines (1/2),Print some lines (1/3)] 
--> line 2.2 
[Print a set of lines (2/2),Print some lines (1/3)] 
[Print some lines (2/3)] 
--> line 3 
[Print some lines (3/3)] 

वास्तविक क्रियान्वयन (एक टिप्पणी की संस्करण के लिए this देखें):

type Progress l = ProgressT l Identity 

runProgress :: Progress l a 
       -> Either (Progress l a, TaskStack l) a 
runProgress = runIdentity . runProgressT 

newtype ProgressT l m a = 
    ProgressT 
    { 
    procedure :: 
     Coroutine 
     (Yield (TaskStack l)) 
     (StateT (TaskStack l) m) a 
    } 

instance MonadTrans (ProgressT l) where 
    lift = ProgressT . lift . lift 

instance Monad m => Monad (ProgressT l m) where 
    return = ProgressT . return 
    p >>= f = ProgressT (procedure p >>= procedure . f) 

instance MonadIO m => MonadIO (ProgressT l m) where 
    liftIO = lift . liftIO 

runProgressT :: Monad m 
       => ProgressT l m a 
       -> m (Either (ProgressT l m a, TaskStack l) a) 
runProgressT action = do 
    result <- evalStateT (resume . procedure $ action) [] 
    return $ case result of 
    Left (Yield stack cont) -> Left (ProgressT cont, stack) 
    Right a -> Right a 

type TaskStack l = [Task l] 

data Task l = 
    Task 
    { taskLabel :: l 
    , taskTotalSteps :: Word 
    , taskStep :: Word 
    } deriving (Show, Eq) 

task :: Monad m 
     => l 
     -> Word 
     -> ProgressT l m a 
     -> ProgressT l m a 
task label steps action = ProgressT $ do 
    -- Add the task to the task stack 
    lift . modify $ pushTask newTask 

    -- Perform the procedure for the task 
    result <- procedure action 

    -- Insert an implicit step at the end of the task 
    procedure step 

    -- The task is completed, and is removed 
    lift . modify $ popTask 

    return result 
    where 
    newTask = Task label steps 0 
    pushTask = (:) 
    popTask = tail 

step :: Monad m => ProgressT l m() 
step = ProgressT $ do 
    (current : tasks) <- lift get 
    let currentStep = taskStep current 
     nextStep = currentStep + 1 
     updatedTask = current { taskStep = nextStep } 
     updatedTasks = updatedTask : tasks 
    when (currentStep > taskTotalSteps current) $ 
    fail "The task has already completed" 
    yield updatedTasks 
    lift . put $ updatedTasks 
2

ऐसा करने का सबसे स्पष्ट तरीका StateT के साथ है।

import Control.Monad.State 

type ProgressT m a = StateT Int m a 

step :: Monad m => ProgressT m() 
step = modify (subtract 1) 

मुझे यकीन है कि तुम क्या task के शब्दों होना चाहते हैं लेकिन, नहीं कर रहा हूँ ... दिखाने के लिए

संपादित करें कि कैसे आप आईओ साथ

step :: (Monad m, MonadIO m) => ProgressT m() 
step = do 
    modify (subtract 1) 
    s <- get 
    liftIO $ putStrLn $ "steps remaining: " ++ show s 

ऐसा करने चाहते हैं ध्यान दें कि आपको राज्य को मुद्रित करने के लिए MonadIO बाधा की आवश्यकता होगी। यदि आपको राज्य के साथ एक अलग प्रभाव की आवश्यकता है तो आपके पास एक अलग प्रकार की बाधा हो सकती है (यानी यदि कोई कदम शून्य से नीचे चला जाता है, या जो कुछ भी हो तो अपवाद फेंक दें)।

+0

यह उपयोगी नहीं होगा, क्योंकि प्रक्रिया समाप्त होने के बाद ही किसी को राज्य तक पहुंच प्राप्त होगी, जो प्रगति को ट्रैक करने की अनुमति नहीं देता है। – dflemstr

+0

हू? आप राज्य को पढ़ने के लिए किसी भी समय 'get' पर कॉल कर सकते हैं! – sclv

+0

यदि मेरे पास 'प्रक्रिया :: स्टेटटी इंट आईओ() है; प्रक्रिया = हमेशा के लिए चरण ', मैं 'प्रक्रिया' कैसे चला सकता हूं ताकि उदाहरण के लिए, हर बार' चरण 'कहा जाता है, वर्तमान चरण मान मुद्रित करता है? 'राज्य 'मोनड के साथ यह संभव नहीं है। – dflemstr

1

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

अद्यतन:

अब शेष चरणों की संख्या निकाल सकते हैं। निम्नलिखित के साथ -XRebindableSyntax

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 

module Test where 

import Prelude hiding (Monad(..)) 
import qualified Prelude as Old (Monad(..)) 

----------------------------------------------------------- 

data Z = Z 
data S n = S 

type Zero = Z 
type One = S Zero 
type Two = S One 
type Three = S Two 
type Four = S Three 

----------------------------------------------------------- 

class Peano n where 
    peano :: n 
    fromPeano :: n -> Integer 

instance Peano Z where 
    peano = Z 
    fromPeano Z = 0 

instance Peano (S Z) where 
    peano = S 
    fromPeano S = 1 

instance Peano (S n) => Peano (S (S n)) where 
    peano = S 
    fromPeano s = n `seq` (n + 1) 
    where 
     prev :: S (S n) -> (S n) 
     prev S = S 
     n = fromPeano $ prev s 

----------------------------------------------------------- 

class (Peano s, Peano p) => Succ s p | s -> p where 
instance Succ (S Z) Z where 
instance Succ (S n) n => Succ (S (S n)) (S n) where 

----------------------------------------------------------- 

infixl 1 >>=, >> 

class ParameterisedMonad m where 
    return :: a -> m s s a 
    (>>=) :: m s1 s2 t -> (t -> m s2 s3 a) -> m s1 s3 a 
    fail :: String -> m s1 s2 a 
    fail = error 

(>>) :: ParameterisedMonad m => m s1 s2 t -> m s2 s3 a -> m s1 s3 a 
x >> f = x >>= \_ -> f 

----------------------------------------------------------- 

newtype PIO p q a = PIO { runPIO :: IO a } 

instance ParameterisedMonad PIO where 
    return = PIO . Old.return 
    PIO io >>= f = PIO $ (Old.>>=) io $ runPIO . f 

----------------------------------------------------------- 

data Progress p n a = Progress a 

instance ParameterisedMonad Progress where 
    return = Progress 
    Progress x >>= f = let Progress y = f x in Progress y 

runProgress :: Peano n => n -> Progress n Zero a -> a 
runProgress _ (Progress x) = x 

runProgress' :: Progress p Zero a -> a 
runProgress' (Progress x) = x 

task :: Peano n => n -> Progress n n() 
task _ = return() 

task' :: Peano n => Progress n n() 
task' = task peano 

step :: Succ s n => Progress s n() 
step = Progress() 

stepsLeft :: Peano s2 => Progress s1 s2 a -> (a -> Integer -> Progress s2 s3 b) -> Progress s1 s3 b 
stepsLeft prog f = prog >>= flip f (fromPeano $ getPeano prog) 
    where 
    getPeano :: Peano n => Progress s n a -> n 
    getPeano prog = peano 

procedure1 :: Progress Three Zero String 
procedure1 = do 
    task' 
    step 
    task (peano :: Two) -- any other Peano is a type error 
    --step -- uncommenting this is a type error 
    step -- commenting this is a type error 
    step 
    return "hello" 

procedure2 :: (Succ two one, Succ one zero) => Progress two zero Integer 
procedure2 = do 
    task' 
    step `stepsLeft` \_ n -> do 
    step 
    return n 

main :: IO() 
main = runPIO $ do 
    PIO $ putStrLn $ runProgress' procedure1 
    PIO $ print $ runProgress (peano :: Four) $ do 
    n <- procedure2 
    n' <- procedure2 
    return (n, n') 
+0

यह एक बहुत अच्छा समाधान है, लेकिन यह एक अलग समस्या हल करता है। मूल प्रश्न में कृपया मेरा ** संपादित ** देखें। – dflemstr

+0

@dflemstr: –

+0

अपडेट किया गया यह अभी भी एक अलग समस्या हल करता है।किसी भी तरह से प्रगति चरणों को स्थिर रूप से गवाह करना महत्वपूर्ण नहीं है। और 'प्रक्रिया x = कार्य "foo" x कर रहे हैं। forM_ [1..x] $ const const step 'इस समाधान के साथ असंभव हो जाता है। [यह समाधान] (http://stackoverflow.com/a/8568374/230461) समस्या हल करता है, लेकिन आदर्श नहीं हो सकता है। – dflemstr

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