Skip to content

Instantly share code, notes, and snippets.

@cboudereau
Last active September 25, 2017 15:27
Show Gist options
  • Save cboudereau/0b48ae359323db305fcd2beeb8d37a10 to your computer and use it in GitHub Desktop.
Save cboudereau/0b48ae359323db305fcd2beeb8d37a10 to your computer and use it in GitHub Desktop.
//The aim of this gist is to answer to a code review during the fsharp mentor program.
//The given code uses result in a way where Ok and Error and into the same type causing a lots of match
//This version supply a begin of workflow by separating Ok case from Error case and supplying an adapter function called adapt in order to
// map legacy domain to the new domain.
//Consider the existing code base represented as an union case :
type [<Struct>] TransportId = TransportId of string
type Transport =
| Transport of TransportId //Happy path
| UnknownTransport of TransportId //A business error
//Now the plumbing part that map the legacy domain (the union with success and error) and the new domain which separates the domain
type TransportFailure =
| UnknownTransport of TransportId
let adapt = function
| Transport.Transport x -> x |> Ok
| Transport.UnknownTransport x -> TransportFailure.UnknownTransport x |> Error
let legacyFailure = TransportId "hello!" |> Transport.UnknownTransport
let legacySuccess = TransportId "hello!" |> Transport.Transport
//Those functions are monadic style because they take a simple argument ie the string and return an augmented type (the Result type here)
let sendEmail (TransportId id) =
printfn "sending email with transport id %s" id
TransportId id |> Ok
let workflowAdapter = adapt >> Result.bind sendEmail
//This sample will not send an email
legacyFailure |> workflowAdapter
(*Ouput from f# interactive
[<Struct>]
val it : Result<unit,TransportFailure> =
Error (UnknownTransport (TransportId "hello!"))*)
//This sample will send an email
legacySuccess |> workflowAdapter
(*sending email with transport id hello!
[<Struct>]
val it : Result<unit,TransportFailure> = Ok ()*)
//Here is a sample with config technical parameter
let checkConfiguration config (TransportId x) =
printfn "checking configuration for %s with config" x
Ok ()
let prodCheckConfiguration = checkConfiguration "prod"
//This is a kleisli operator for f o g on elevated types (like Result)
let (>=>) f g = f >> Result.bind g
//a more composed workflow
let biggerWorkflow =
workflowAdapter
>=> prodCheckConfiguration
//Don't send an email and do not check
(*[<Struct>]
val it : Result<unit,TransportFailure> =
Error (UnknownTransport (TransportId "hello!"))*)
biggerWorkflow legacyFailure
//Send an email and check config if everything is ok
biggerWorkflow legacySuccess
(*
sending email with transport id hello!
checking configuration for hello! with config
[<Struct>]
val it : Result<unit,TransportFailure> = Ok ()*)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment