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
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.
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 img1
s 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.
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 >>=
).
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
.