Yesterday, I rewrote the following:

type

StateR<'state, 'ret, 'err> = StateRM of ('state -> RetState<'state,'ret,'err>)

and

RetState<'state,'ret,'err> =

| Success of 'ret * 'state

| Fail of Err<'state,'err>

and

Err<'state,'err> =

| ErrMsg of 'err*'state

| ErrOr of Err<'state,'err>*Err<'state,'err>

| ErrAnd of Err<'state,'err>*Err<'state,'err>

| ErrTag of 'err*Err<'state,'err>

let rec mapErrState f = functionTo understand the adapt' and adapt function take a look a the monadic bind function:

| ErrMsg(et,s) -> ErrMsg(et, f s)

| ErrOr(e1, e2) -> ErrOr(mapErrState f e1, mapErrState f e2)

| ErrAnd(e1, e2) ->ErrAnd(mapErrState f e1, mapErrState f e2)

| ErrTag(et, e) -> ErrTag(et, mapErrState f e)

let adapt' get set err m' = fun a -> toState (fun s ->

match (runState (m' a)) (get s) with

| Success (r,s') -> Success (r, set s s')

| Fail es -> Fail (err (mapErrState (fun s' ->set s s') es))

)

let adapt get set err m = toState (fun s ->

match (runState m) (get s) with

| Success (r,s') -> Success (r, set s s')

| Fail es -> Fail (err (mapErrState (fun s' ->set s s') es))

)

let bind m f = toState (fun s ->Here, runstate gets the function out of the monad m and applies it to the state s. This normally results in a success "pair" with the second term being the new state and the first term of the pair being the optional return type of the monad (is unit type when no return).

match runState m s with

| Success (v,s')->

let n = f v

runState n s'

| Fail err ->

Fail err)

What adapt does is to adapt a monad to work with a different state, normally a larger state that includes a the state of the monad. Now I can rewrite the following functions:

let map1M' m' = adapt' Lib.first (fun (_,s) s' -> (s',s)) Lib.identity m'These functions are used to locally grow the monadic state into a pair where the second arument is the original state and the first is provided as an argument (pairM and pairM') and then to run "local" monads that are defined either for the first or second state. The ' annotation indicates that we are working with functions that return monads and not "raw" state monads.

let map1M m = adapt Lib.first (fun (_,s) s' -> (s',s)) Lib.identity m

let map2M' m' = adapt' Lib.second (fun (s,_) s' -> (s,s')) Lib.identity m'

let map2M m = adapt Lib.second (fun (s,_) s' -> (s,s')) Lib.identity m

let pairM' t m' = adapt' (fun s -> (t, s)) (fun s (t,s') -> s') Lib.identity m'

let pairM t m = adapt (fun s -> (t, s)) (fun s (t,s') -> s') Lib.identity m

This brings me back to the my on Local versus global monadic states in large applications, a third solution to integrate a monadic module is to write the module purely with a local state and then to "adapt" it into a larger state with the functions above. The limitations of doing things this way is that is that these modules cannot call other modules as they have no other state to make available than their own.

Finally, you may note those "get and set" arguments for the adapt functions. These are not ideal but I think I need to rewrite my error management to remove my explicit state from the failure type. My gut feeling tell me that would allow a "two way" interation where the parameterizing argument to adapt could itself be a monad.

## No comments:

Post a Comment