There are a bunch of IO computations whose result don't matter, and that rely on a same context variable. I would like an aesthetical way to pass this argument to all functions.
Say that I want to print a char several times
c = 'c'
main = do
putChar c
putChar c
putChar c
but I don't want to write the parameter each time.
The following works just fine:
samearg :: (Foldable t, Monad m) => t (a -> m ()) -> a -> m ()
samearg fs ctx = foldl (\d f -> d >>= \_ -> f ctx) (return ()) fs
(>^=) = flip samearg
'c' >^= [putChar,putChar,putChar]
Now, I'm just curious whether I could have written things according to my initial idea, or whether there is some standard way to do this. I wanted to write something like 'c' >^= putChar >^= putChar >^= putChar that would reduce to this
((return 'c' >>= putChar >>= \_ -> return 'c')
>>= putChar >>= \_ -> return 'c')
>>= putChar
but this operator I wrote isn't reducing to what I expected
(>^=) :: Monad m => m b -> (b -> m a) -> m b
(>^=) ctx f = ctx >>= f >>= \_ -> ctx
return 'c' >^= putChar >^= putChar >^= putChar
which I understand, but I'm still wondering whether I could have made it work.
(This makes the possibly too-optimistic assumption that all the functions have a return type of IO ().)
putChar has type Char -> IO (). There are three types here, all of which have Monoid instances:
base-4.9) () <> () == ()base-4.10) If b is a monoid, then IO b is as well; m1 <> m2 == (liftA2 (<>)) m1 m2 (a new IO action, in which the results of executing the original IO actions are combined, is returned).base-4.9) If b is a monoid, then for f,g :: a -> b we have f <> g == \x -> f x <> g x (both functions are called on the same argument and the return values are combined.Putting this all together, functions of type Char -> IO () form a monoid.
> :t putChar <> putChar <> putChar
putChar <> putChar <> putChar :: Char -> IO ()
> (putChar <> putChar <> putChar) 'c'
ccc>
So you can simply write
main = putChar <> putChar <> putChar $ c
or
main = mconcat (replicate 3 putChar) $ c
Here's a fixed version of (>^=)
(>^=) :: Monad m => m b -> (b -> m a) -> m b
(>^=) ctx f = ctx >>= \y -> f y >> return y -- only uses ctx once
You can also put the actions in a list and traverse it, using existing combinators:
traverse_ (\go -> go 'c') [putChar, putChar, putChar]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With