Skip to content

Commit

Permalink
Added F# data structures like (Reader and State monads) as well as T… (
Browse files Browse the repository at this point in the history
…#67)

* Added F#  data structures like (Reader and State monads) as well as Traversable implementations
  • Loading branch information
fraliv13 authored Mar 10, 2020
1 parent 7002054 commit 1b8eab4
Show file tree
Hide file tree
Showing 11 changed files with 331 additions and 26 deletions.
18 changes: 9 additions & 9 deletions NBB.sln
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,6 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "NBB.Core.Effects.FSharp", "
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MultiTenancy", "MultiTenancy", "{0ECF24A0-BFED-4F7A-8B06-CC40E628E852}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "NBB.Contracts.FSharp", "samples\MicroServices\NBB.Contracts\NBB.Contracts.FSharp\NBB.Contracts.FSharp.fsproj", "{2ED572D2-0DF3-4B09-B061-3CBA329CA5B1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NBB.MultiTenancy.Identification.Tests", "test\UnitTests\MultiTenancy\NBB.MultiTenancy.Identification.Tests\NBB.MultiTenancy.Identification.Tests.csproj", "{64275FFB-A07C-43C2-8D7F-21DDA18B1DF3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NBB.MultiTenancy.Identification.Http", "src\MultiTenancy\NBB.MultiTenancy.Identification.Http\NBB.MultiTenancy.Identification.Http.csproj", "{5EED8D24-86E1-4F9C-81F5-541A8CF892D5}"
Expand All @@ -356,9 +354,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NBB.Messaging.MultiTenancy.
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "NBB.Core.Evented.FSharp", "src\Core\NBB.Core.Evented.FSharp\NBB.Core.Evented.FSharp.fsproj", "{560D2A62-21C9-42FB-977C-7F2E66A1A529}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "NBB.Core.Effects.FSharp.Tests", "test\UnitTests\Core\NBB.Core.Effects.FSharp.Tests\NBB.Core.Effects.FSharp.Tests.fsproj", "{41925593-33AD-4E12-A7C4-71A50A15C44B}"
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "NBB.Core.Effects.FSharp.Tests", "test\UnitTests\Core\NBB.Core.Effects.FSharp.Tests\NBB.Core.Effects.FSharp.Tests.fsproj", "{41925593-33AD-4E12-A7C4-71A50A15C44B}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "NBB.Core.Evented.FSharp.Tests", "test\UnitTests\Core\NBB.Core.Evented.FSharp.Tests\NBB.Core.Evented.FSharp.Tests.fsproj", "{16EC08F8-DC9B-4D8E-89BA-6A8FBD74265B}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "NBB.Core.Evented.FSharp.Tests", "test\UnitTests\Core\NBB.Core.Evented.FSharp.Tests\NBB.Core.Evented.FSharp.Tests.fsproj", "{16EC08F8-DC9B-4D8E-89BA-6A8FBD74265B}"
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "NBB.Core.FSharp", "src\Core\NBB.Core.FSharp\NBB.Core.FSharp\NBB.Core.FSharp.fsproj", "{06E25E78-E1B6-4237-8D48-2A4EFBD73240}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -800,10 +800,6 @@ Global
{826D8C5A-AC92-4559-9C82-629666525D6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{826D8C5A-AC92-4559-9C82-629666525D6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{826D8C5A-AC92-4559-9C82-629666525D6C}.Release|Any CPU.Build.0 = Release|Any CPU
{2ED572D2-0DF3-4B09-B061-3CBA329CA5B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2ED572D2-0DF3-4B09-B061-3CBA329CA5B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2ED572D2-0DF3-4B09-B061-3CBA329CA5B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2ED572D2-0DF3-4B09-B061-3CBA329CA5B1}.Release|Any CPU.Build.0 = Release|Any CPU
{64275FFB-A07C-43C2-8D7F-21DDA18B1DF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64275FFB-A07C-43C2-8D7F-21DDA18B1DF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64275FFB-A07C-43C2-8D7F-21DDA18B1DF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -844,6 +840,10 @@ Global
{16EC08F8-DC9B-4D8E-89BA-6A8FBD74265B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16EC08F8-DC9B-4D8E-89BA-6A8FBD74265B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16EC08F8-DC9B-4D8E-89BA-6A8FBD74265B}.Release|Any CPU.Build.0 = Release|Any CPU
{06E25E78-E1B6-4237-8D48-2A4EFBD73240}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{06E25E78-E1B6-4237-8D48-2A4EFBD73240}.Debug|Any CPU.Build.0 = Debug|Any CPU
{06E25E78-E1B6-4237-8D48-2A4EFBD73240}.Release|Any CPU.ActiveCfg = Release|Any CPU
{06E25E78-E1B6-4237-8D48-2A4EFBD73240}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -999,7 +999,6 @@ Global
{DB768569-2CDA-4382-84E3-D41494A22EDD} = {F170EE3D-296D-487B-9498-CCD8541BAFB2}
{826D8C5A-AC92-4559-9C82-629666525D6C} = {14726095-DA28-43A6-A9A9-F16C605932E1}
{0ECF24A0-BFED-4F7A-8B06-CC40E628E852} = {90E022FB-CA1B-49DD-9BEA-CE7F8E74E8BB}
{2ED572D2-0DF3-4B09-B061-3CBA329CA5B1} = {A820C4C2-1472-46ED-BA95-47B588B1D7AF}
{64275FFB-A07C-43C2-8D7F-21DDA18B1DF3} = {0ECF24A0-BFED-4F7A-8B06-CC40E628E852}
{5EED8D24-86E1-4F9C-81F5-541A8CF892D5} = {F170EE3D-296D-487B-9498-CCD8541BAFB2}
{45ECA8AA-9AF9-430C-A73D-016FE11596FA} = {0ECF24A0-BFED-4F7A-8B06-CC40E628E852}
Expand All @@ -1010,6 +1009,7 @@ Global
{560D2A62-21C9-42FB-977C-7F2E66A1A529} = {14726095-DA28-43A6-A9A9-F16C605932E1}
{41925593-33AD-4E12-A7C4-71A50A15C44B} = {29B7593C-60F4-41DC-A883-4976FF467927}
{16EC08F8-DC9B-4D8E-89BA-6A8FBD74265B} = {29B7593C-60F4-41DC-A883-4976FF467927}
{06E25E78-E1B6-4237-8D48-2A4EFBD73240} = {14726095-DA28-43A6-A9A9-F16C605932E1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {23A42379-616A-43EF-99BC-803DF151F54E}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ module Domain =
module Data =
module Contract =
open Domain.ContractAggregate
let loadById contractId = Effect.pureEffect { ContractId = contractId; Value = 78m; Status = Draft; }
let save (contract:Contract) = Effect.pureEffect contract |> Effect.ignore
let loadById contractId = Effect.pure' { ContractId = contractId; Value = 78m; Status = Draft; }
let save (contract:Contract) = Effect.pure' contract |> Effect.ignore


module Application =
Expand Down
66 changes: 66 additions & 0 deletions src/Core/NBB.Core.Effects.FSharp/Data/ReaderEffect.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
namespace NBB.Core.Effects.FSharp.Data.ReaderEffect

open NBB.Core.Effects.FSharp
open NBB.Core.FSharp.Data
open NBB.Core.FSharp.Data.Reader

type ReaderEffect<'s, 't> = 's -> Effect<'t>
module ReaderEffect =
let run (x: ReaderEffect<'s, 't>) : 's -> Effect<'t> =
x

let map (f: 't->'u) (m : ReaderEffect<'s, 't>) : ReaderEffect<'s,'u> =
m >> Effect.map f

let bind (f: 't-> ReaderEffect<'s, 'u>) (m : ReaderEffect<'s, 't>) : ReaderEffect<'s, 'u> =
fun s -> Effect.bind (fun a -> run (f a) s) (run m s)

let apply (f: ReaderEffect<'s, ('t -> 'u)>) (m: ReaderEffect<'s, 't>) : ReaderEffect<'s, 'u> =
fun s -> Effect.bind (fun g -> Effect.map (fun (a: 't) -> (g a)) (run m s)) (f s)

let pure' x =
fun _ -> Effect.pure' x

let lift (eff : Effect<'t>) : ReaderEffect<'s, 't> =
fun _ -> eff

let hoist (reader : Reader<'s, 't>) : ReaderEffect<'s, 't> =
fun s -> Effect.pure' (reader s)

module ReaderEffectBulder =
type ReaderEffectBulder() =
member _.Bind (m, f) = ReaderEffect.bind f m : ReaderEffect<'s,'u>
member _.Return x = ReaderEffect.pure' x : ReaderEffect<'s,'u>
member _.ReturnFrom x = x : ReaderEffect<'s,'u>
member _.Combine (m1, m2) = ReaderEffect.bind (fun _ -> m1) m2 : ReaderEffect<'s,'u>
member _.Zero () = ReaderEffect.pure' () : ReaderEffect<'s, unit>

[<AutoOpen>]
module ReaderEffectExtensions =
let readerEffect = new ReaderEffectBulder.ReaderEffectBulder()

let (<!>) = ReaderEffect.map
let (<*>) = ReaderEffect.apply
let (>>=) eff func = ReaderEffect.bind func eff


[<RequireQualifiedAccess>]
module List =
let traverseReaderEffect f list =
let pure' = ReaderEffect.pure'
let (<*>) = ReaderEffect.apply
let cons head tail = head :: tail
let initState = pure' []
let folder head tail = pure' cons <*> (f head) <*> tail
List.foldBack folder list initState

let sequenceReaderEffect list = traverseReaderEffect id list

[<RequireQualifiedAccess>]
module Result =
let traverseReaderEffect (f: 'a-> ReaderEffect<'s, 'b>) (result:Result<'a,'e>) : ReaderEffect<'s, Result<'b, 'e>> =
match result with
|Error err -> ReaderEffect.pure' (Error err)
|Ok v -> ReaderEffect.map Result.Ok (f v)

let sequenceReaderEffect result = traverseReaderEffect id result
76 changes: 76 additions & 0 deletions src/Core/NBB.Core.Effects.FSharp/Data/StateEffect.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
namespace NBB.Core.Effects.FSharp.Data.StateEffect

open NBB.Core.Effects.FSharp
open NBB.Core.FSharp.Data
open NBB.Core.FSharp.Data.State

type StateEffect<'s, 't> = 's -> Effect<'t * 's>
module StateEffect =
let run (x: StateEffect<'s, 't>) : 's -> Effect<'t * 's> =
x

let map (f: 't->'u) (m : StateEffect<'s, 't>) : StateEffect<'s,'u> =
fun s -> Effect.map (fun (a, s') -> (f a, s')) (run m s)

let bind (f: 't-> StateEffect<'s, 'u>) (m : StateEffect<'s, 't>) : StateEffect<'s, 'u> =
fun s -> Effect.bind (fun (a, s') -> run (f a) s') (run m s)

let apply (f: StateEffect<'s, ('t -> 'u)>) (m: StateEffect<'s, 't>) : StateEffect<'s, 'u> =
fun s -> Effect.bind (fun (g, s') -> Effect.map (fun (a: 't, s'': 's) -> ((g a), s'')) (run m s')) (f s)

let pure' x =
fun s -> Effect.pure' (x, s)

let get () : StateEffect<'s, 's> =
fun s -> Effect.pure' (s, s)

let put (x: 's) : StateEffect<'s, unit> =
fun _ -> Effect.pure' ((), x)

let flatten x =
bind id x

let lift (eff : Effect<'t>) : StateEffect<'s, 't> =
fun s -> eff |> Effect.map (fun a -> (a, s))

let hoist (state: State<'s, 't>) : StateEffect<'s, 't> =
fun s -> Effect.pure' (State.run state s)

module StateEffectBulder =
type StateEffectBulder() =
member _.Bind (m, f) = StateEffect.bind f m : StateEffect<'s,'u>
member _.Return x = StateEffect.pure' x : StateEffect<'s,'u>
member _.ReturnFrom x = x : StateEffect<'s,'u>
member _.Combine (m1, m2) = StateEffect.bind (fun _ -> m1) m2 : StateEffect<'s,'u>
member _.Zero () = StateEffect.pure' () : StateEffect<'s, unit>

let stateEffectssss = new StateEffectBulder()

[<AutoOpen>]
module StateEffectExtensions =
let stateEffect = new StateEffectBulder.StateEffectBulder()

let (<!>) = StateEffect.map
let (<*>) = StateEffect.apply
let (>>=) eff func = StateEffect.bind func eff

[<RequireQualifiedAccess>]
module List =
let traverseStateEffect f list =
let pure' = StateEffect.pure'
let (<*>) = StateEffect.apply
let cons head tail = head :: tail
let initState = pure' []
let folder head tail = pure' cons <*> (f head) <*> tail
List.foldBack folder list initState

let sequenceStateEffect list = traverseStateEffect id list

[<RequireQualifiedAccess>]
module Result =
let traverseStateEffect (f: 'a-> StateEffect<'s, 'b>) (result:Result<'a,'e>) : StateEffect<'s, Result<'b, 'e>> =
match result with
| Error err -> StateEffect.map Result.Error (StateEffect.pure' err)
| Ok v -> StateEffect.map Result.Ok (f v)

let sequenceStateEffect result = traverseStateEffect id result
36 changes: 23 additions & 13 deletions src/Core/NBB.Core.Effects.FSharp/Effects.fs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ module Effect =

let ignore eff = map (fun _ -> ()) eff

//let composeK f g x = bind g (f x)
//let lift2 f = map f >> apply
let composeK f g x = bind g (f x)
let lift2 f = map f >> apply

let interpret<'a> (interpreter:IInterpreter) (Effect eff) = interpreter.Interpret<'a>(eff) |> Async.AwaitTask

Expand Down Expand Up @@ -60,17 +60,27 @@ module EffectBuilder =
module Effects =
let effect = new EffectBuilder.EffectBuilder()

//let (<!>) = Effect.map
//let (<*>) = Effect.apply
//let (>>=) eff func = Effect.bind func eff
//let (>=>) = Effect.composeK
let (<!>) = Effect.map
let (<*>) = Effect.apply
let (>>=) eff func = Effect.bind func eff
let (>=>) = Effect.composeK


//module List =
// let traverseEffect f list =
// let cons head tail = head :: tail
// let initState = Effect.pure' []
// let folder head tail = Effect.pure' cons <*> (f head) <*> tail
// List.foldBack folder list initState
[<RequireQualifiedAccess>]
module List =
let traverseEffect f list =
let cons head tail = head :: tail
let initState = Effect.pure' []
let folder head tail = Effect.pure' cons <*> (f head) <*> tail
List.foldBack folder list initState

// let sequenceEffect list = traverseEffect id list
let sequenceEffect list = traverseEffect id list

[<RequireQualifiedAccess>]
module Result =
let traverseEffect (f: 'a-> Effect<'c>) (result:Result<'a,'e>) : Effect<Result<'c, 'e>> =
match result with
| Error err -> Effect.pure' (Error err)
| Ok v -> Effect.map Ok (f v)

let sequenceEffect result = traverseEffect id result
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@

<ItemGroup>
<Compile Include="Effects.fs" />
<Compile Include="Data\ReaderEffect.fs" />
<Compile Include="Data\StateEffect.fs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\NBB.Core.Effects\NBB.Core.Effects.csproj" />
<ProjectReference Include="..\NBB.Core.FSharp\NBB.Core.FSharp\NBB.Core.FSharp.fsproj" />
</ItemGroup>

</Project>
60 changes: 60 additions & 0 deletions src/Core/NBB.Core.FSharp/NBB.Core.FSharp/Data/Reader.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
namespace NBB.Core.FSharp.Data.Reader

type Reader<'s, 't> = 's -> 't
module Reader =
let run (x: Reader<'s, 't>) : 's -> 't =
x

let map (f: 't->'u) (m : Reader<'s, 't>) : Reader<'s,'u> =
m >> f

let bind (f: 't-> Reader<'s, 'u>) (m : Reader<'s, 't>) : Reader<'s, 'u> =
fun s -> let a = run m s in run (f a) s

let apply (f: Reader<'s, ('t -> 'u)>) (m: Reader<'s, 't>) : Reader<'s, 'u> =
fun s -> let f = run f s in let a = run m s in f a

let ask : Reader<'s, 's> =
id

let pure' x =
fun _ -> x


module ReaderBulder =
type ReaderBulder() =
member _.Bind (m, f) = Reader.bind f m : Reader<'s,'u>
member _.Return x = Reader.pure' x : Reader<'s,'u>
member _.ReturnFrom x = x : Reader<'s,'u>
member _.Combine (m1, m2) = Reader.bind (fun _ -> m1) m2 : Reader<'s,'u>
member _.Zero () = Reader.pure' () : Reader<'s, unit>

[<AutoOpen>]
module ReaderExtensions =
let reader = new ReaderBulder.ReaderBulder()

let (<!>) = Reader.map
let (<*>) = Reader.apply
let (>>=) st func = Reader.bind func st


[<RequireQualifiedAccess>]
module List =
let traverseReader f list =
let pure' = Reader.pure'
let (<*>) = Reader.apply
let cons head tail = head :: tail
let initState = pure' []
let folder head tail = pure' cons <*> (f head) <*> tail
List.foldBack folder list initState

let sequenceReader list = traverseReader id list

[<RequireQualifiedAccess>]
module Result =
let traverseReader (f: 'a-> Reader<'s, 'b>) (result:Result<'a,'e>) : Reader<'s, Result<'b, 'e>> =
match result with
| Error err -> Reader.pure' (Error err)
| Ok v -> Reader.map Result.Ok (f v)

let sequenceReader result = traverseReader id result
16 changes: 16 additions & 0 deletions src/Core/NBB.Core.FSharp/NBB.Core.FSharp/Data/ResultExtensions.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace NBB.Core.FSharp.Data

[<AutoOpen>]
module ResultExtensions =

[<RequireQualifiedAccess>]
module List =
let traverseResult f list =
let pure' = Result.Ok
let (<*>) fn = Result.bind (fun x-> Result.map (fun f -> f x) fn)
let cons head tail = head :: tail
let initState = pure' []
let folder head tail = pure' cons <*> (f head) <*> tail
List.foldBack folder list initState

let sequenceResult list = traverseResult id list
Loading

0 comments on commit 1b8eab4

Please sign in to comment.