Last active
September 25, 2017 15:27
-
-
Save cboudereau/0b48ae359323db305fcd2beeb8d37a10 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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