+Ken Shan's paper [Monads for natural language semantics](http://arxiv.org/abs/cs/0205026v1) (2001) discusses how to systematically move from some base monads to the corresponding monad transformers. But as he notes, his algorithm isn't the only one possible, and it only applies to monads whose type has a certain form. (Reader and State have that form; List for example doesn't.)
+
+As best we know, figuring out how a monad transformer should be defined is still something of an art, not something that can be done mechanically. However, you can think that all of the art goes into deciding what StateT and so on should be; having figured that out, plain State would follow as the simple case where StateT is parameterized on the Identity monad.
+
+Apart from whose interface is outermost, the behavior of a StateT(Maybe) and a MaybeT(State) will partly coincide. But in certain crucial respects they will diverge, and you need to think carefully about which behavior you want and what the appropriate layering is for your needs. Consider these examples:
+
+ # module MS = Maybe_monad.T(S);;
+ # module SM = S.T(Maybe_monad);;
+ # MS.(run (elevate (S.puts succ) >> zero () >> elevate S.get >>= fun cur -> unit (cur+10) )) 0;;
+ - : int option * S.store = (None, 1)
+ # MS.(run (elevate (S.puts succ) >> zero () >> elevate (S.put 5) )) 0;;
+ - : unit option * S.store = (None, 1)
+
+Although we have a wrapped `None`, notice that the store (as it was at the point of failure) is still retrievable.
+
+ # SM.(run (puts succ >> elevate (Maybe_monad.zero ()) >> get >>= fun cur -> unit (cur+10) )) 0;;
+ - : ('a, int * S.store) Maybe_monad.result = None
+
+When Maybe is on the inside, on the other hand, a failure means the whole computation has failed, and even the store is no longer available.
+
+<!--
+ # ES.(run( elevate (S.puts succ) >> throw "bye" >> elevate S.get >>= fun i -> unit(i+10) )) 0;;
+ - : int Failure.error * S.store = (Failure.Error "bye", 1)
+ # SE.(run( puts succ >> elevate (Failure.throw "bye") >> get >>= fun i -> unit(i+10) )) 0;;
+ - : (int * S.store) Failure.result = Failure.Error "bye"
+ # ES.(run_exn( elevate (S.puts succ) >> throw "bye" >> elevate S.get >>= fun i -> unit(i+10) )) 0;;
+ Exception: Failure "bye".
+ # SE.(run_exn( puts succ >> elevate (Failure.throw "bye") >> get >>= fun i -> unit(i+10) )) 0;;
+ Exception: Failure "bye".
+-->
+
+Here's an example wrapping Maybe around List, and vice versa:
+
+ # module LM = List_monad.T(Maybe_monad);;
+ # module ML = Maybe_monad.T(List_monad);;
+ # ML.(run (plus (zero ()) (unit 20) >>= fun i -> unit (i+10)));;
+ - : ('_a, int) ML.result = [Some 30]
+
+When List is on the inside, the failed results just get dropped and the computation proceeds without them.
+
+ # LM.(run (plus (elevate (Maybe_monad.zero ())) (unit 20) >>= fun i -> unit (i+10)));;
+ - : ('_a, int) LM.result = None
+
+On the other hand, when Maybe is on the inside, failures abort the whole computation.
+
+<!--
+ # EL.(run( plus (throw "bye") (unit 20) >>= fun i -> unit(i+10)));;
+ - : int EL.result = [Failure.Error "bye"; Failure.Success 30]
+ # LE.(run( plus (elevate (Failure.throw "bye")) (unit 20) >>= fun i -> unit(i+10)));;
+ - : int LE.result = Failure.Error "bye"
+ # EL.(run_exn( plus (throw "bye") (unit 20) >>= fun i -> unit(i+10)));;
+ Exception: Failure "bye".
+ # LE.(run_exn( plus (elevate (Failure.throw "bye")) (unit 20) >>= fun i -> unit(i+10)));;
+ Exception: Failure "bye".
+-->
+
+This is fun. Notice the difference it makes whether the second `plus` is native to the outer `List_monad`, or whether it's the inner `List_monad`'s `plus` elevated into the outer wrapper:
+
+ # module LL = List_monad.T(List_monad);;