
On Fri, Oct 06, 2006 at 11:40:46AM +0200, Martin Grabmueller wrote:
I hereby announce a small tutorial on using monad transformers. In contrast to others found on the web, it concentrates on using them, not on their implementation.
I'd like to hear comments, suggestions, etc. about it!
I have found it generally correct and pleasant to read, and my co-worker, who is just learning to use monad transformers, said he found it helpful. The problem for him was that he was reading it away from the computer, so he couldn't check the types of various functions/methods used by you - like liftIO. Perhaps you could warn that it's better to read it near a running Haskell interpreter, or present the type signatures in an appendix at the end. No let's get to nitpicking ;-) None of the following issues is very serious, but maybe they will help you improve your article. * page 5, at the end of section 2.1: it might be good to explicitly say that "eval1 Map.empty exampleExp" typed at ghci prompt uses the IO monad. Also, I think only the recent ghci versions will print the result of an IO action, which may be confusing for the readers * page 7, sixth line in section 2.3: I think that talking about "threading" the value fits State monad better. For the Reader monad I would rather say something like: "A reader monad pushes a value to all (sub)computations", but this may be my personal bias. * page 8, at about 85%: you say "The state maintained in our example is a simple integer value, but it *can* be any data type we wish". I think it's better to say *could*, because with *can* it could be (mis)understood that in a give State monad use the type of state can change. Also, you are mixing values with types: "simple integer value" vs "any data type". * page 9: why not make "tick" polymorphic, so you won't have to rewrite it later (tick' and tick'') ? * page 9: about style of eval4: it seems a bit error prone to have to call "tick" in every case. I would try to split eval4 into two mutually recursive functions: one that does the tick, and the other that does the actual work and calls the first one recursively. But then there is a risk of calling the wrong one of the two functions. I would try to avoid it by giving the functions incompatible types. Well, OK, this could divert the readers' focus from monad transformers... ;-) * first code fragment on the page 4: you could write: runEval1 = runIdentity omitting the arg, but this might be a deliberate choice * page 10, in the middle: "documentation for the WriterT monad" - I would add "... transformer". * page 11, at about 40%: "because Identity is the innermost monad". I think "because Identity is our base monad" would be more appropriate. possible typos: * page 8, in the middle: missing left parenthesis in "type r -> r) -> ..." * page 6, at about 60%, in "Map.lookup returns it result" replace "it" with "its" * page 6, at about 75%, there is something wrong with "we" in "... even shorter (better?) we exploiting the fact ..." Best regards Tomasz