From 671856a2a8d9ec4f76dd4dc6cee94619ea637568 Mon Sep 17 00:00:00 2001 From: Grzegorz Dziadkiewicz Date: Sat, 30 Apr 2022 11:54:06 +0200 Subject: [PATCH 1/2] Add State monad sample. --- FSharpx.Extras.sln | 1 + docs/StateSample.fsx | 100 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 docs/StateSample.fsx diff --git a/FSharpx.Extras.sln b/FSharpx.Extras.sln index fdaaf8b3..eae6300a 100644 --- a/FSharpx.Extras.sln +++ b/FSharpx.Extras.sln @@ -14,6 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{A6A6AF7D-D docs\index.fsx = docs\index.fsx docs\Santa.fsx = docs\Santa.fsx docs\StmSample.fsx = docs\StmSample.fsx + docs\StateSample.fsx = docs\StateSample.fsx docs\StructuredFormatSample.fsx = docs\StructuredFormatSample.fsx docs\UndoSample.fsx = docs\UndoSample.fsx docs\WebProxy.fsx = docs\WebProxy.fsx diff --git a/docs/StateSample.fsx b/docs/StateSample.fsx new file mode 100644 index 00000000..4a94e62e --- /dev/null +++ b/docs/StateSample.fsx @@ -0,0 +1,100 @@ +#r @"../bin/FSharpx.Extras.dll" +#r @"../bin/FSharpx.Collections.dll" + +open System +open System.Threading +open FSharpx + +type MyStateRecord = + { + X:int + Y:string + } +type MyResultType = + { + R: double + } + +let ``nested step without result`` : State.State = + State.state { + printfn "Starting ``nested step without result``" + + let! s = State.getState + do! State.putState {s with X = s.X+1} + printfn "Ending ``nested step without result``" + } + +let ``nested step with same state type, but different result type`` : State.State = + State.state { + printfn "Starting ``nested step with same state type, but different result type``" + + printfn "Getting the state sn1 inside nested step " + let! sn1 = State.getState + printfn "sn1: %A" sn1 + + printfn "Writing state inside nested step" + do! State.putState {sn1 with X=0; Y="nested Step"} + printfn "Getting the state sn2 inside nested step " + let! sn2 = State.getState + + printfn "Notice that put does not mutate sn1" + printfn "sn1: %A" sn1 + printfn "sn2: %A" sn2 + + printfn "Ending ``nested step with same state type, but different result type``" + return (char sn1.X) + } + +let ``computation using state monad`` : State.State = + State.state { + printfn "Starting ``computation using state monad``" + printfn "Getting state value s1" + let! s1 = State.getState + printfn "s1: %A" s1 + + printfn "Writing state" + do! State.putState {s1 with Y = "first put"} + printfn "Getting state value s2" + let! s2 = State.getState + printfn "s2: %A" s2 + + printfn "Start nested step resulting in c1" + let! c1 = ``nested step with same state type, but different result type`` + printfn "End nested step resulting in c1" + printfn "Getting state value s3" + let! s3 = State.getState + printfn "c1: %A, s3: %A" c1 s3 + + printfn "Start nested step without result (increases X by one)" + do! ``nested step without result`` + printfn "End nested step resulting" + printfn "Getting state value s4" + let! s4 = State.getState + printfn "s4: %A" s4 + + printfn "double c1: %A double s4.X: %A" (double c1) (double s4.X) + printfn "Ending ``computation using state monad``" + return {R= 123.0 + double c1 + double s4.X} + } + +let run () = + let startingState = {X = 42; Y="start"} + + printfn "You can get both result and state by providing the State monad with start state" + printfn "(Notice that computation won't start until you provide `startState`)" + let (result, endState) = ``computation using state monad`` startingState + printfn "result: %A, endState: %A" result endState + printfn "" + printfn "" + printfn "" + printfn "Or result only with `eval`" + printfn "(Notice recomputation)" + let resultOnly = State.eval ``computation using state monad`` startingState + printfn "resultOnly: %A" resultOnly + printfn "" + printfn "" + printfn "" + printfn "Or endState only with `exec`" + let onlyEndState = State.exec ``computation using state monad`` startingState + printfn "onlyEndState: %A" onlyEndState + () \ No newline at end of file From f381f077c6a8c8fac7a254d63077457cd6289cb6 Mon Sep 17 00:00:00 2001 From: Grzegorz Dziadkiewicz Date: Mon, 23 May 2022 18:00:44 +0200 Subject: [PATCH 2/2] Add documentation for State monad functions --- src/FSharpx.Extras/ComputationExpressions/State.fs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/FSharpx.Extras/ComputationExpressions/State.fs b/src/FSharpx.Extras/ComputationExpressions/State.fs index 4a8200a6..49a06d16 100644 --- a/src/FSharpx.Extras/ComputationExpressions/State.fs +++ b/src/FSharpx.Extras/ComputationExpressions/State.fs @@ -7,11 +7,17 @@ module State = type State<'T, 'State> = 'State -> 'T * 'State + /// Returns state value from the monad. let getState = fun s -> (s,s) + /// Replaces state inside the monad. let putState s = fun _ -> ((),s) + /// Evaluates a state computation with the given initial state and returns the final value, discarding the final state. let eval m s = m s |> fst + /// Evaluates a state computation with the given initial state and returns the final state, discarding the final value. let exec m s = m s |> snd + /// Instance of state monad without value let empty = fun s -> ((), s) + /// Sequentially compose two actions, passing any value produced by the first as an argument to the second. let bind k m = fun s -> let (a, s') = m s in (k a) s' /// The state monad. @@ -68,12 +74,12 @@ module State = let inline (>=>) f g = fun x -> f x >>= g /// Right-to-left Kleisli composition let inline (<=<) x = flip (>=>) x - - let foldM f s = + /// Fold encapsulated in the State monad + let foldM f s = Seq.fold (fun acc t -> acc >>= (flip f) t) (returnM s) - + /// Evaluates each monadic action in the list from left to right, and collects the results. let inline sequence s = let inline cons a b = lift2 List.cons a b List.foldBack cons s (returnM []) - + /// Maps each element of a list to a monadic action, evaluates these actions from left to right, and collects the results. let inline mapM f x = sequence (List.map f x) \ No newline at end of file