Monad Input/Output and Monad Transformers

1. Learning haskell I discovered that I/O should be avoided nearly 'at all costs'. The problem is that the IO monad is the only one which have more interactive work flow. There is Reader/Writer monad but in fact AFAIU first one is about the environment and second one is about logging. Implementation of Writer can be defined in terms of handles but it is rather limited (either non-full implementation which may be confusing or need of caching the result for pass etc.). I searched the hackage but I didn't find package with pure I/O. Such package may look like: class (Monad m, Monoid v) => MonadInput v m where -- | Gets an element from input (line of text [with \n], 4096 bytes, -- or something like that). mzero on end getChunk :: m v class (Monad m, Monoid v) => MonadOutput v m where -- | Puts an element putChunk :: v -> m () In similar way filters (for example buffered input) can be defined: class (MonadInput v m) => MonadBufferedInput m where -- | If not whole chunk has been consumed at once (for example only -- first 3 elements from list) rest can be returned. It will be -- returned as part of the input on next getChunk call. returnChunk :: v -> m () data (MonadInput v m, Monoid v) => BufferedInputT v m a = BufferedInputT a v Also pipes may be defined (as far as I understand but I'm not 100% sure) which probably will simplify the writing of network tests: -- | Evaluates the first argument. If the getChunk is called the -- evaluation is passed to second argument until the putChunk is -- called, which argument is returned in the first argument callPipeT :: (Monad m, Monoid v) => PipeInputT v m a -> PipeOutputT v m b -> m (a, b) I've started some tests but I'd be grateful for comments (well - probably I'm not the first who come to this idea so a) there is such package or b) my level of Haskell does not allow me to see the problems). 2. I find writing monad transformers annoying. Additionally if package defines transformer A and another transformer B they need to be connected 'by hand'. I find a simple solution which probably is incorrect as it hasn't been used: instance (MonadState s n, Monad (m n), MonadTrans m) => MonadState s (m n) where get = lift get put = lift . put (requires FlexibleInstances MultiParamTypeClasses FlexibleContexts UndecidableInstances - two last are not extensions used by mtl) Regards

Hello Maciej, Thursday, July 2, 2009, 3:31:59 PM, you wrote:
class (Monad m, Monoid v) => MonadInput v m where -- | Gets an element from input (line of text [with \n], 4096 bytes, -- or something like that). mzero on end getChunk :: m v class (Monad m, Monoid v) => MonadOutput v m where -- | Puts an element putChunk :: v -> m ()
how about interact function? -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Thu, 2009-07-02 at 15:43 +0400, Bulat Ziganshin wrote:
Hello Maciej,
Thursday, July 2, 2009, 3:31:59 PM, you wrote:
class (Monad m, Monoid v) => MonadInput v m where -- | Gets an element from input (line of text [with \n], 4096 bytes, -- or something like that). mzero on end getChunk :: m v class (Monad m, Monoid v) => MonadOutput v m where -- | Puts an element putChunk :: v -> m ()
how about interact function?
Well. As far as I know there is no way of using it with network. Additionally there is hard to put monadic code in it: myFunc :: (MonadInput i, MonadOutput o) => (String -> m a) -> MyMonad i o m [a] If m == IO - which may be a case in normal code it requires unsafePerformIO with all it's problems. In testing I can use pipes and Identity simplifying the whole testing - allowing user to use it's own monads. The other problem is that the order sometimes matters. Consider: main = interact (\x -> "What's your name?\n" ++ "Hello:\n" ++ x) For human being it is annoying but sometimes it is for example against RFC. Regards

On Thu, Jul 2, 2009 at 5:31 AM, Maciej Piechotka
2. I find writing monad transformers annoying. Additionally if package defines transformer A and another transformer B they need to be connected 'by hand'.
You have not given any concrete problems or examples, so it's hard for me to comment. But at first glance, I would conjecture that you are relying too heavily on monads and sequential thinking. Consider what your code would look like without a single monad. Obviously you cannot talk to the network without IO, but your program can still be * modeled* purely, and then toss in IO at the last second to tie it to the network. This model may be difficult for you because it requires your brain to be rewired; feel free to mail this list with concrete modeling problems and we will help you out. As for the pure model, throw away Reader, Writer, State -- everything, and just use pure functions. Then add monads back in *at small scopes* when they clean things up. I used to approach problems by designing a monad for my whole program, using an appropriate stack of transformers. I suspect such an approach led to the claim that "monads are not appropriate for large software systems" in a popular paper a few months ago. As I have gained more experience, I found that this is the *wrong* way to go about using them. Now my primary use of monads is within the scope of a single function, to tie together the helper functions in the where clause. Luke
I find a simple solution which probably is incorrect as it hasn't been used:
instance (MonadState s n, Monad (m n), MonadTrans m) => MonadState s (m n) where get = lift get put = lift . put
(requires FlexibleInstances MultiParamTypeClasses FlexibleContexts UndecidableInstances - two last are not extensions used by mtl)
Regards
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Thu, Jul 2, 2009 at 1:18 PM, Luke Palmer
I used to approach problems by designing a monad for my whole program, using an appropriate stack of transformers. I suspect such an approach led to the claim that "monads are not appropriate for large software systems" in a popular paper a few months ago.
Link please! I googled but I couldn't find it :( I'd like to find out what you and the authors have learned about the inappropriateness of monads for large software systems. Thanks, Jason

Hello Luke, Friday, July 3, 2009, 12:18:21 AM, you wrote:
I used to approach problems by designing a monad for my whole program, using an appropriate stack of transformers. I suspect such an approach led to the claim that "monads are not appropriate for large software systems" in a popular paper a few months ago. As I have gained more experience, I found that this is the wrong way to go about using them.
was it ghc authors paper? :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Thu, 2009-07-02 at 14:18 -0600, Luke Palmer wrote:
On Thu, Jul 2, 2009 at 5:31 AM, Maciej Piechotka
wrote: 2. I find writing monad transformers annoying. Additionally if package defines transformer A and another transformer B they need to be connected 'by hand'. You have not given any concrete problems or examples, so it's hard for me to comment. But at first glance, I would conjecture that you are relying too heavily on monads and sequential thinking.
Consider what your code would look like without a single monad. Obviously you cannot talk to the network without IO, but your program can still be modeled purely, and then toss in IO at the last second to tie it to the network. This model may be difficult for you because it requires your brain to be rewired; feel free to mail this list with concrete modeling problems and we will help you out.
As for the pure model, throw away Reader, Writer, State -- everything, and just use pure functions. Then add monads back in at small scopes when they clean things up.
AFAIU you comment the 2de point only. I look at this moment from library, not program point of view. So consider the library IOMonad which defined some MonadInput v m monad. Then you have NNTP library which has NntpT m monad. Each of them defines appropriate stack such as that NntpT (State s) is instance of MonadState. But if there is NntpT MyInput, where MyInput isinstance of MonadInput, is not MonadInput. To do it with current approach the libraries would have to be interlinked. Also it is quite boring to include for all monad instance ... => ... where f1 = lift f1 f2 = lift f2 ...
I used to approach problems by designing a monad for my whole program, using an appropriate stack of transformers.
I thought about others which might have want to use my monads in their functions...
I suspect such an approach led to the claim that "monads are not appropriate for large software systems" in a popular paper a few months ago.
I'd appreciate the link - google find nothing. I fall in love in Haskell about a week or two ago and I fall in love just after I started learning it ;)
As I have gained more experience, I found that this is the wrong way to go about using them. Now my primary use of monads is within the scope of a single function, to tie together the helper functions in the where clause.
Luke
Regards

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 On Thu, Jul 2, 2009 at 5:05 PM, Maciej Piechotka wrote:
I'd appreciate the link - google find nothing. I fall in love in Haskell about a week or two ago and I fall in love just after I started learning it ;)
"Research programming languages like Haskell [22] and ML [20] didn't seem to offer any near-term solution. Diatchki's work on fine-grain representation in Haskell [25] is not yet main-stream, and had not yet started when we began work on BitC. Support for state in Haskell exists in the form of the I/O monad [23], but in our opinion the monadic idiom does not scale well to large, complexly stateful programs,1 and imposes constraints that are unnatural in the eyes of systems programmers." Oh, and not only do our monads not scale, they're slow to boot: " Ultimately, the problem with Haskell and ML for our purposes is that the brightest and most aggressive programmers in those languages, using the most aggressive optimization techniques known to the research community, remain unable to write systems codes that compete reasonably with C or C++. The most successful attempt to date is probably the FoxNet TCP/IP protocol stack, which incurred a 10x increase in system load and a 40x penalty in accessing external memory relative to a conventional (and less aggressively optimized) C implemenation. [ 4 ,6 ]" http://www.bitc-lang.org/docs/bitc/bitc-origins.html - -- gwern -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEAREKAAYFAkpNJWoACgkQvpDo5Pfl1oLpeQCcDXUnfBaitwii3rhortVqO8Fr SXIAnAiKY5EGg/ssZHOaooP1ag1xGIE4 =iugB -----END PGP SIGNATURE-----

Gwern Branwen wrote: ...
" Ultimately, the problem with Haskell and ML for our purposes is that the brightest and most aggressive programmers in those languages, using the most aggressive optimization techniques known to the research community, remain unable to write systems codes that compete reasonably with C or C++. The most successful attempt to date is probably the FoxNet TCP/IP protocol stack, which incurred a 10x increase in system load and a 40x penalty in accessing external memory relative to a conventional (and less aggressively optimized) C implemenation. [ 4 ,6 ]"
Interesting paper. Putting these remarks in context, in case anyone takes them as a current critique of Haskell, they are apparently about ten years out-of-date and apply to this SML program http://www.cs.cmu.edu/~fox/foxnet.html I wonder what would happen if the program was ported and benchmarked in a recent version of GHC. Richard.
participants (6)
-
Bulat Ziganshin
-
Gwern Branwen
-
Jason Dagit
-
Luke Palmer
-
Maciej Piechotka
-
Richard Kelsall