How to write a monad that prints “step i of N” when executing each statement in the monad?

后端 未结 5 1396
清歌不尽
清歌不尽 2021-02-15 10:38

I\'m not even sure this is possible in any kind of monad; does it violate monad laws? But it seems like something that should be possible in some kind of construct or other. S

5条回答
  •  南笙
    南笙 (楼主)
    2021-02-15 11:25

    There are two ways that this might violate the laws, depending on what you mean.

    For example, if return were to count as a step, then you'd have a violation because the first monad law would not hold:

    do x <- return  /=  f x
       f x
    

    Similarly, if abstracting out two steps into another named function counts as removing a step, then you also violate the monad laws, because the third monad law would not hold:

    m' = do x <- m
            f x
    
    do y <- m'  /=  do x <- m
       g y             y <- f x
                       g y
    

    However, if you have commands explicitly emit the "step" output, then there is no violation. This is because return could then not emit any output at all, and sequencing two commands would just add their step outputs together. Here's an example:

    import Control.Monad.Trans.State
    import Control.Monad.Trans.Class (lift)
    
    step :: StateT Int IO ()
    step = do
        n <- get
        lift $ putStrLn $ "Step " ++ show n
        put (n + 1)
    
    command1 = do
        command1'  -- The command1 logic without the step behavior
        step
    
    command2 = do
        command2'
        step
    
    -- etc.
    

    Note that I don't include the total number of steps. There's no way to have access to that information for a monad. For that I recommend Daniel's answer, because Applicatives are an excellent solution to this problem of determining the number of steps statically without any Template Haskell.

提交回复
热议问题