Precise flow control in Haskell

后端 未结 2 1265
生来不讨喜
生来不讨喜 2021-02-02 10:58

The Idea

Hello! I\'m trying to implement in Haskell an image processing library based on dataflow ideology. I\'ve got a problem connected to how I want

2条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-02-02 11:34

    A Monad can not reorder the component steps that make up img1 and img2 in

    addImage :: (Monad m) => m [i] -> m [i] -> m [i]
    addImage img1 img2 = do
        i1 <- img1
        i2 <- img2
        return $ i1 ++ i2
    

    if there exists any m [i] whose result depends on a side effect. Any MonadIO m has an m [i] whose result depends on a side effect, therefore you cannot reorder the component steps of img1 and img2.

    The above desugars to

    addImage :: (Monad m) => m [i] -> m [i] -> m [i]
    addImage img1 img2 =
        img1 >>=
            (\i1 ->
                img2 >>=
                    (\i2 ->
                        return (i1 ++ i2)
                    )
            )
    

    Let's focus on the first >>= (remembering that (>>=) :: forall a b. m a -> (a -> m b) -> m b). Specialized for our type, this is (>>=) :: m [i] -> ([i] -> m [i]) -> m [i]. If we are going to implement it, we'd have to write something like

    (img1 :: m [i]) >>= (f :: [i] -> m [i]) = ... 
    

    In order to do anything with f, we need to pass it an [i]. The only correct [i] we have is stuck inside img1 :: m [i]. We need the result of img1 to do anything with f. There are now two possibilities. We either can or can not determine the result of img1 without executing its side effects. We will examine both cases, starting with when we can not.

    can not

    When we can not determine the result of img1 without executing its side effects, we have only one choice - we must execute img1 and all of its side effects. We now have an [i], but all of img1s side effects have already been executed. There's no way we can execute any of the side effects from img2 before some of the side effects of img1 because the side effects of img1 have already happened.

    can

    If we can determine the result of img1 without executing its side effects, we're in luck. We find the result of img1 and pass that to f, getting a new m [i] holding the result we want. We can now examine the side effects of both img1 and the new m [i] and reorder them (although there's a huge caveat here about the associative law for >>=).

    the problem at hand

    As this applies to our case, for any MonadIO, there exists the following, whose result can not be determined without executing its side effects, placing us firmly in the can not case where we can not re-order side effects.

    counterExample :: (MonadIO m) => m String
    counterExample = liftIO getLine
    

    There are also many other counter examples, such as anything like readImage1 or readImage2 that must actually read the image from IO.

提交回复
热议问题