forever :: m () -> m Void; (>>) :: m () -> m a -> m a
It should be made clear when information is thrown away, for the benefit of the writer, reader and automatic code makers like exference. forever :: IO Int -> IO () discards the Int implicitly, this should be made explicit via void :: m a -> m (). The () is conjured from nothing (or rather, from Void), because forever does not ever return a value. Using its output value is absurd and thus should be made explicit via fmap absurd :: m Void -> m ().
The second suggestion might be more controversion, but is made in the same vein. This would also imply that do notation would complain if you insert lines of type m a with a not being () without binding them to something. When would you ever want to not bind them to something, anyway? I can only think of not needing the thread id returned by forkIO. (And thus, there should be forkIO_. Hmm. Does this belong in a new issue?)
This would also imply that do notation would complain if you insert lines of type
m awithanot being()without binding them to something.
Well, GHC already has this as a warning (which becomes an error with -Wall).
When would you ever want to not bind them to something, anyway? I can only think of not needing the thread id returned by
forkIO.
I can also think of:
-
char :: Char -> Parser Charandsatisfy :: (Char -> Bool) -> Parser Char(very often) -
takeMVar :: MVar a -> IO awhen used to empty theMVar -
getLine :: IO Stringwhen it's used to implement something simple like “press Enter to continue”
There are some cases where -implicitly throwing information away- hurts performance, tho: _ <- mapM is slower than mapM_; in such cases forbidding implicit throwing-away would be nice. Otherwise, I don't remember having been ever bitten by this (and so I always turn the -fwarn-unused-do-bind flag off), and I like return values that I can optionally use (my argument for them is the same as my argument for optional parameters in other languages).
I remember that there were several discussions on mailing lists about changing the type of >>, but I can't find them right now (if you can, please post them!).
Yea, those all sound like they ought to be made explicit with _ or void, although I'm not sure about char/satisfy. The mapM_ reminds me:
- mapM_ :: (Monad m, Foldable t) => (a -> m ()) -> t a -> m ()
- traverse_ :: (Applicative f, Foldable t) => (a -> f ()) -> t a -> f ()
A-a-and a discussion about changing the type of forM_ has been started on the libraries mailing list: https://mail.haskell.org/pipermail/libraries/2016-March/026860.html (not by me, but still).