X-Git-Url: http://lambda.jimpryor.net/git/gitweb.cgi?p=lambda.git;a=blobdiff_plain;f=topics%2F_week9_state_monad_tutorial.mdwn;h=14628f7b1e043988ea19dc8219e13007b92cd8a3;hp=def82baeab21e50e231169de3bf0a92db23a3ccb;hb=2d413395c1eb768b35a9ecf409aa7730ab89d629;hpb=8071cf9cb642dd55d1d58f6f7afd85a5ff11b5e3 diff --git a/topics/_week9_state_monad_tutorial.mdwn b/topics/_week9_state_monad_tutorial.mdwn index def82bae..14628f7b 100644 --- a/topics/_week9_state_monad_tutorial.mdwn +++ b/topics/_week9_state_monad_tutorial.mdwn @@ -16,7 +16,7 @@ The most lightweight way encapsulate it would be just to add a data constructor (* we assume that the store type has already been declared *) type 'a state = State of (store -> ('a * store)) -Then a function expecting an `'a store` will look for a value with the structure `State ...` rather than just one with the structure `...`. +Then a function expecting an `'a state` will look for a value with the structure `State ...` rather than just one with the structure `...`. To take a `State (s -> (a,s))` and get at the `s -> (a,s)` it wraps, you use the same techniques you use to take an `Some int` and get at the `int` it wraps: @@ -38,7 +38,7 @@ with `run xx`. So you should only apply `run` when you've finished building up a Of course you can do this: - let xx = someMonad.(...) in + let xx = SomeMonad.(...) in let intermediate_result = SomeMonad.run xx 0 in let yy = SomeMonad.(xx >>= ...) in let final_result = SomeMonad.run yy 0 in @@ -76,9 +76,9 @@ OK, back to our walk-through of "A State Monad Tutorial". What shall we use for type store' = { total : int; modifications: int };; -State monads employing this store will then have *three* salient values at any point in the computation: the `total` and `modifications` field in the store, and also the `'a` value that is then wrapped in the monadic box. +State monads employing this store will then have *three* salient values at any point in the computation: the `total` and `modifications` field in the store, and also the `'a` payload that is currently wrapped in the monadic box. -Here's a monadic box that encodes the operation of incrementing the store's `total` and wrapping the value that was the former `total`: +Here's a monadic value that encodes the operation of incrementing the store's `total` and wrapping the value that was the former `total`: let increment_store : store' -> (int * store') = fun s -> let value = s.total in @@ -99,9 +99,9 @@ Here is how you'd have to do it using our OCaml/Juli8 monad library: # module S = Monad.State(struct type store = store' end);; # let increment_store'' : 'a S.t = S.(get >>= fun cur -> - let value = cur.total - in let s' = { total = succ cur.total; modifications = succ cur.modifications } - in put s' >> mid value);; + let value = cur.total in + let s' = { total = succ cur.total; modifications = succ cur.modifications } in + put s' >> mid value);; Let's try it out: @@ -111,7 +111,7 @@ Let's try it out: Or if you used the OCaml/Juli8 monad library: - # S.(run increment_store'') s0;; + # S.run increment_store'' s0;; - : int * S.store = (42, {total = 43; modifications = 4}) Great! @@ -120,7 +120,7 @@ Can you write a monadic value that instead of incrementing each of the `total` a What about a value that increments each of `total` and `modifications` twice? Well, you could custom-write that, as with the previous question. But we already have the tools to express it easily, using our existing `increment_store` value: - increment_store >>= fun value -> increment_store >> unit value + increment_store >>= fun value -> increment_store >> mid value That ensures that the value we get at the end is the value returned by the first application of `increment_store`, that is, the contents of the `total` field in the store before we started modifying the store at all. @@ -136,19 +136,19 @@ or, using pattern-matching on the record (you don't have to specify every field But **the point of learning how to do this monadically** is that (1) monads show us how to embed more sophisticated programming techniques, such as imperative state and continuations, into frameworks that don't natively possess them (such as the set-theoretic metalanguage of Groenendijk, Stokhof and Veltman's paper); (2) becoming familiar with monads will enable you to see patterns you'd otherwise miss, and implement some seemingly complex computations using the same simple patterns (same-fringe is an example); and finally, of course (3) monads are delicious. -Keep in mind that the final result of a bind chain doesn't have to be the same type as the starting value: +Keep in mind that the final result of a `mbind` chain doesn't have to be the same type as the starting value: - increment_store >>= fun value -> increment_store >> unit (string_of_int value) + increment_store >>= fun value -> increment_store >> mid (string_of_int value) Or: - unit 1 >> unit "blah" + mid 1 >> mid "blah" The store keeps the same type throughout the computation, but the type of the wrapped value can change. -What are the special-purpose operations that the `State_monad` module defines for us? +What are the special-purpose operations that the `Monad.State` module defines for us? -* `get` is a monadic value that passes through the existing store unchanged, and also wraps that same store as its boxed value. You use it like this: +* `get` is a monadic value that passes through the existing store unchanged, and also wraps that same store as its boxed payload. You use it like this: ... >>= fun _ -> get >>= fun cur -> ... here we can use cur ... @@ -164,33 +164,33 @@ What are the special-purpose operations that the `State_monad` module defines fo ... >> gets (fun cur -> cur.total) >>= fun total -> ... - For more complex structured stores, consider using the `Ref_monad` version of the State monad in the OCaml library. + For more complex structured stores, consider using the `Monad.Ref` variant of the State monad in the OCaml library. * `put new_store` replaces the existing store with `new_store`. Use it like this: ... >> put new_store >> fun () -> ... - As that code snippet suggests, the boxed value after the application of `modify new_store` is just `()`. If you want to preserve the existing boxed value but replace the store, do this: + As that code snippet suggests, the boxed payload after the application of `modify new_store` is just `()`. If you want to preserve the existing payload but replace the store, do this: - ... >>= fun value -> put new_store >> unit value >>= ... + ... >>= fun value -> put new_store >> mid value >>= ... -* Finally, `modify modifier` applies `modifier` to whatever the existing store is, and substitutes that as the new store. As with `put`, the boxed value afterwards is `()`. +* Finally, `modify modifier` applies `modifier` to whatever the existing store is, and substitutes that as the new store. As with `put`, the boxed payload afterwards is `()`. Here's an example from "A State Monad Tutorial": increment_store >> get >>= fun cur -> State (fun s -> ((), { total = s.total / 2; modifications = succ s.modifications })) >> - increment_store >> unit cur.total + increment_store >> mid cur.total Or, as you'd have to write it using our OCaml monad library: increment_store'' >> get >>= fun cur -> put { total = cur.total / 2; modifications = succ cur.modifications } >> - increment_store'' >> unit cur.total + increment_store'' >> mid cur.total The last topic covered in "A State Monad Tutorial" is the use of do-notation to work with monads in Haskell. We discuss that on our [translation page](/translating_between_OCaml_Scheme_and_Haskell).