GenCore


Use of the Result monad

To use the Result monad open up th following library

1: 
open Informedica.GenCore.Lib

Open up the Result operators

1: 
open Informedica.GenCore.Lib.Result.Operators

Sample domain object Person

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
63: 
64: 
module Person =

    open WrappedString

    // Dto to create a Person
    module Dto =

        type Dto = { Id: string; Name: string }

        let create id nm = { Id = id; Name = nm }

    // Person messages mapped from
    // Id and Name messages
    module Message = 

        type IdMessage =
            | IdInfo of Id.Message.Info
            | NameInfo of Name.Message.Info

        type WarnMessage =
            | IdWarn of Id.Message.Warning
            | NameWarn of Name.Message.Warning

        type ErrMessage =
            | IdErr of Id.Message.Error
            | NameErr of Name.Message.Error


    type Person = { Id: WrappedString.Id.Id; Name: WrappedString.Name.Name }

    // Create a Person using Id and Name domain types
    let create id nm = { Id = id; Name = nm }

    // Create a Person from a Dto
    let fromDto (dto: Dto.Dto) =
        let ctId id =
            // Map Id messages to Person messages
            let map msg = 
                match msg with
                | Message.Info i    -> i |> Message.IdInfo |> Message.Info
                | Message.Warning w -> w |> Message.IdWarn |> Message.Warning
                | Message.Error e   -> e |> Message.IdErr |> Message.Error
                | Message.Except e  -> e |> Message.Except

            id 
            |> WrappedString.Id.create 1 10
            |> Result.mapMessagesR map

        let ctNm nm = 
            // Map Id messages to Person messages
            let map msg = 
                match msg with
                | Message.Info i    -> i |> Message.NameInfo |> Message.Info
                | Message.Warning w -> w |> Message.NameWarn |> Message.Warning
                | Message.Error e   -> e |> Message.NameErr |> Message.Error
                | Message.Except e  -> e |> Message.Except

            nm
            |> WrappedString.Name.create 1 20
            |> Result.mapMessagesR map

        create
        <!> ctId dto.Id
        <*> ctNm dto.Name

Creating a Person, the happy path

1: 
2: 
3: 
4: 
5: 
6: 
7: 
Person.Dto.create "1" "Frank"
|> Person.fromDto

   Succ
     ({Id = Id "1";
       Name = Name "Frank";},
      [[Info (IdInfo (Info "1"))]; [Info (NameInfo (Info "Frank"))]])

Trying to create a Person with empty strings fails

1: 
2: 
3: 
4: 
5: 
Person.Dto.create "" ""
|> Person.fromDto

  Fail
    [[Error (IdErr (MinLength ("",1)))]; [Error (NameErr (MinLength ("",1)))]]

Also, the Id cannot be too large, but creating a Name succeeded

1: 
2: 
3: 
4: 
5: 
6: 
Person.Dto.create "11111111111111111111" "Frank"
|> Person.fromDto

  Fail
    [[Error (IdErr (MaxLength ("11111111111111111111",10)))];
     [Info (NameInfo (Info "Frank"))]]

A Name cannot contain non-letter characters

1: 
2: 
3: 
4: 
Person.Dto.create "1" "1"
|> Person.fromDto

  Fail [[Error (NameErr (NoLetters "1"))]; [Info (IdInfo (Info "1"))]]

Creating Person succeeded, but both Id and Name had trailing spaces and where trimmed

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
Person.Dto.create "1 " "  Frank  "
|> Person.fromDto

  Succ
    ({Id = Id "1";
      Name = Name "Frank";},
     [[Warning (IdWarn (Changed ("1 ","1"))); Info (IdInfo (Info "1"))];
      [Warning (NameWarn (Changed ("  Frank  ","Frank")));
       Info (NameInfo (Info "Frank"))]])
namespace Informedica
namespace Informedica.GenCore
namespace Informedica.GenCore.Lib
module Result

from Informedica.GenCore.Lib
module Operators

from Informedica.GenCore.Lib.ResultModule


 Contains the infix operators for
 
 * `>>=` bindL: bind a `Result` to a function that processes the contents of the result
 * `<<=` bindR: bind a function that processes the contents of a result to a `Result`
 * `<!>` liftR: lift a normal function to process and return a `Result`, note the function
 should be a 'non failing' function without messages
 * `<*>` applyR: apply a `Result` to a function that processes and returns a `Result`
module WrappedString

from Informedica.GenCore.Lib


 Types and functions to deal with
 value primitives
type Dto =
  {Id: string;
   Name: string;}

Full name: Tutorial.Person.Dto.Dto
Multiple items
Dto.Id: string

--------------------
module Id

from Informedica.GenCore.Lib.WrappedString


 Type and functions that
 deal with an identifier
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
Multiple items
Dto.Name: string

--------------------
module Name

from Informedica.GenCore.Lib.WrappedString


 Type and functions that represent and deal
 with the `Name` type
val create : id:string -> nm:string -> Dto

Full name: Tutorial.Person.Dto.create
val id : string
val nm : string
module Id

from Informedica.GenCore.Lib.WrappedString


 Type and functions that
 deal with an identifier
module Name

from Informedica.GenCore.Lib.WrappedString


 Type and functions that represent and deal
 with the `Name` type
module Message

from Informedica.GenCore.Lib


 Type and functions that model and process
 a `Message` to be used with the `Result` monad
type IdMessage =
  | IdInfo of Info
  | NameInfo of Info

Full name: Tutorial.Person.Message.IdMessage
union case IdMessage.IdInfo: Id.Message.Info -> IdMessage
module Message

from Informedica.GenCore.Lib.WrappedString.IdModule


 Messsages for `Id`
Multiple items
union case Id.Message.Info.Info: string -> Id.Message.Info

--------------------
type Info = | Info of string

Full name: Informedica.GenCore.Lib.WrappedString.IdModule.Message.Info
union case IdMessage.NameInfo: Name.Message.Info -> IdMessage
module Message

from Informedica.GenCore.Lib.WrappedString.NameModule


 Messsages for `Name`
Multiple items
union case Name.Message.Info.Info: string -> Name.Message.Info

--------------------
type Info = | Info of string

Full name: Informedica.GenCore.Lib.WrappedString.NameModule.Message.Info
type WarnMessage =
  | IdWarn of Warning
  | NameWarn of Warning

Full name: Tutorial.Person.Message.WarnMessage
union case WarnMessage.IdWarn: Id.Message.Warning -> WarnMessage
type Warning = | Changed of string * string

Full name: Informedica.GenCore.Lib.WrappedString.IdModule.Message.Warning
union case WarnMessage.NameWarn: Name.Message.Warning -> WarnMessage
type Warning = | Changed of string * string

Full name: Informedica.GenCore.Lib.WrappedString.NameModule.Message.Warning
type ErrMessage =
  | IdErr of Error
  | NameErr of Error

Full name: Tutorial.Person.Message.ErrMessage
union case ErrMessage.IdErr: Id.Message.Error -> ErrMessage
type Error =
  | MinLength of string * int
  | MaxLength of string * int

Full name: Informedica.GenCore.Lib.WrappedString.IdModule.Message.Error
union case ErrMessage.NameErr: Name.Message.Error -> ErrMessage
type Error =
  | NoLetters of string
  | MinLength of string * int
  | MaxLength of string * int

Full name: Informedica.GenCore.Lib.WrappedString.NameModule.Message.Error
type Person =
  {Id: Id;
   Name: Name;}

Full name: Tutorial.Person.Person
Multiple items
Person.Id: Id.Id

--------------------
module Id

from Informedica.GenCore.Lib.WrappedString


 Type and functions that
 deal with an identifier
Multiple items
union case Id.Id.Id: string -> Id.Id

--------------------
type Id =
  | Id of string
  interface IWrappedValue<string>

Full name: Informedica.GenCore.Lib.WrappedString.IdModule.Id


 Type to represent an identifier
 can be any sinlge line string without
 trailing or preceding spaces.
Multiple items
Person.Name: Name.Name

--------------------
module Name

from Informedica.GenCore.Lib.WrappedString


 Type and functions that represent and deal
 with the `Name` type
Multiple items
union case Name.Name.Name: string -> Name.Name

--------------------
type Name =
  | Name of string
  interface IWrappedValue<string>

Full name: Informedica.GenCore.Lib.WrappedString.NameModule.Name


 Type to represent an identifier
 can be any sinlge line string without
 trailing or preceding spaces.
val create : id:Id.Id -> nm:Name.Name -> Person

Full name: Tutorial.Person.create
val id : Id.Id
val nm : Name.Name
val fromDto : dto:Dto.Dto -> Result.Result<Person,Message.Message<Message.IdMessage,Message.WarnMessage,Message.ErrMessage> list>

Full name: Tutorial.Person.fromDto
val dto : Dto.Dto
module Dto

from Tutorial.Person
val ctId : (string -> Result.Result<Id.Id,Message.Message<Message.IdMessage,Message.WarnMessage,Message.ErrMessage> list>)
val map : (Message.Message<Id.Message.Info,Id.Message.Warning,Id.Message.Error> -> Message.Message<Message.IdMessage,Message.WarnMessage,Message.ErrMessage>)
val msg : Message.Message<Id.Message.Info,Id.Message.Warning,Id.Message.Error>
Multiple items
module Message

from Tutorial.Person

--------------------
module Message

from Informedica.GenCore.Lib


 Type and functions that model and process
 a `Message` to be used with the `Result` monad
union case Message.Message.Info: 'TInfo -> Message.Message<'TInfo,'TWarn,'TErr>
val i : Id.Message.Info
union case Message.IdMessage.IdInfo: Id.Message.Info -> Message.IdMessage
union case Message.Message.Warning: 'TWarn -> Message.Message<'TInfo,'TWarn,'TErr>
val w : Id.Message.Warning
union case Message.WarnMessage.IdWarn: Id.Message.Warning -> Message.WarnMessage
union case Message.Message.Error: 'TErr -> Message.Message<'TInfo,'TWarn,'TErr>
val e : Id.Message.Error
union case Message.ErrMessage.IdErr: Id.Message.Error -> Message.ErrMessage
union case Message.Message.Except: System.Exception -> Message.Message<'TInfo,'TWarn,'TErr>
val e : System.Exception
val create : min:int -> max:int -> (string -> Result.Result<Id.Id,Message.Message<Id.Message.Info,Id.Message.Warning,Id.Message.Error>>)

Full name: Informedica.GenCore.Lib.WrappedString.IdModule.create


 Create an `Id` and return the `Result`
val mapMessagesR : f:('a -> 'b) -> result:Result.Result<'c,'a> -> Result.Result<'c,'b list>

Full name: Informedica.GenCore.Lib.ResultModule.mapMessagesR


 Given an `Result`, map the messages to a different error type.
val ctNm : (string -> Result.Result<Name.Name,Message.Message<Message.IdMessage,Message.WarnMessage,Message.ErrMessage> list>)
val map : (Message.Message<Name.Message.Info,Name.Message.Warning,Name.Message.Error> -> Message.Message<Message.IdMessage,Message.WarnMessage,Message.ErrMessage>)
val msg : Message.Message<Name.Message.Info,Name.Message.Warning,Name.Message.Error>
val i : Name.Message.Info
union case Message.IdMessage.NameInfo: Name.Message.Info -> Message.IdMessage
val w : Name.Message.Warning
union case Message.WarnMessage.NameWarn: Name.Message.Warning -> Message.WarnMessage
val e : Name.Message.Error
union case Message.ErrMessage.NameErr: Name.Message.Error -> Message.ErrMessage
val create : min:int -> max:int -> (string -> Result.Result<Name.Name,Message.Message<Name.Message.Info,Name.Message.Warning,Name.Message.Error>>)

Full name: Informedica.GenCore.Lib.WrappedString.NameModule.create


 Create an `Name` and return the `Result`
Dto.Dto.Id: string
Dto.Dto.Name: string
module Person

from Tutorial
val create : id:string -> nm:string -> Person.Dto.Dto

Full name: Tutorial.Person.Dto.create
val fromDto : dto:Person.Dto.Dto -> Result.Result<Person.Person,Message.Message<Person.Message.IdMessage,Person.Message.WarnMessage,Person.Message.ErrMessage> list>

Full name: Tutorial.Person.fromDto
Fork me on GitHub