SlideShare a Scribd company logo
1 of 29
Understanding F# Workflows New England F# User’s Group Presentation (fsug.org) August 2, 2010 Scott Theleman
Overview F# Workflows – closely related to Monads in Haskell and other languages – are a powerful and elegant tool for solving many real-world problems, though they can be rather daunting at first. We'll survey some ways in which Workflows in the standard F# libraries are used for common development tasks, then dig into detail on how they work. Finally we’ll build a workflow that provides a validation framework that can be used for parsing or other tasks.
Intro to Monads A “Monad” is generally a set of interrelated constructs. At the least, it usually consists of: A “Monadic Type” A bind function (sometimes the (>>=) operator is used) A return function “When the designers of F# talked with the designers of Haskell about this, they agreed that the word monad is obscure and sounds a little daunting and that using other names might be wise” — Expert F# 2.0 (Don Syme, Adam Granicz, Antonio Cisternino)
Characteristics of Monads The Monadic Type “wraps” an underlying type. The monadic type may be more like an object (which may contain other data or state), or more like a computation or potential computation. The Return function wraps an underlying type in a monadic type. The Bind function takes an underlying type as well as a function which maps from the underlying type to a new monadic type, and returns a new monadic type. By performing this wrapping of underlying types inside a monadic type and providing bind and return, you can now combine computations of that inner type in ways that are difficult or impossible when just dealing with the underlying types.
Monad Structure
Uses of Monads aka Workflows
One use of Monads: Sequential Workflows As noted, there are many uses and varieties of Monads We will concentrate on solving a typical sequential workflow style problem First showing other ways this has been done without workflows, then building up to using an F# workflow
Sequential Workflows: If/else The following code takes an initial input (of type T) and performs 3 sets of transformations on it, each time returning a tuple of bool and Result object (of type T). If there is a failure at any step, the entire operation is short circuited. let process1 = true, input // do something with input let process2 = false, input let process3 = true, input   let Execute (input : 'T) =     let ok, result1 = process1 input     if ok then         let ok, result2 = process2 result1         if ok then             let ok, result3 = process3 result2             ok, result3         else false, result2     else false, result1
If/else: Problems The processX() methods and their callers all must know about the input and result types. Generics help the situation, but still these methods are hard-wired for those specific types, plus the success/failure Boolean. Also, the 'T in Execute() and processX() is always the same! It’s getting pretty messy, and we’ve only done 3 transformations. Pretty soon the code is going to be off the right side of the screen! We have to explicitly handle failure at every step of the process Lots of redundancy. We said “ok” 6 times! We don’t have any information about what went wrong.  Though we could define some sort of error type (see next example…).
Sequential Workflows: Option and match The following code tries to improve on the last sample. It now includes a Result<'T> type which we could expand upon to return detailed error information. It also uses pattern matching, which makes the code a bit clearer. type Result<'T> = | Success of 'T | Failure of string   let process1 input = Success(input)  // do something interesting here let process2 input = Failure("Some error") let process3 input = Success(input)    let Process (input : 'T) =     let res1 = process1 input     match res1 with     | Failure _ ->         res1     | Success v ->         let res2 = process2 v         match res2 with         | Failure _ ->             res2         | Success v ->             let res3 = process3 v             res3
Option/match: Problems Better than if/else, but… Still messy and redundant and again the code is drifting off the right side of the screen The processX() methods and their callers still must all know about the input and result types. The 'T in Execute() and processX() is still always the same We still have to explicitly handle failure at every step of the process The Result<'T>type does seem like a nice idea
Sequential Workflows: try/catch Try/catch could simplify/aggregate and improve things a bit – though just for this particular case. It does look nice and streamlined, which is one thing we are looking for. exception MyProcessException of string   let process1 input = input let process2 input = raise <| MyProcessException("An error occurred“) let process3 input = input   // processX now accept and return T // No Result any more; exceptions are used instead let Execute (input : 'T) =     try         let v1 = process1 input         let v2 = process2 v1         let v3 = process3 v2         v3     with     | :? MyProcessException as ex ->         // Catch application-specific error...do we throw or return a Result?? reraise ()     | exn ->         // A "real" exception...what to do here? reraise ()   let Caller<'T> v =     // This will throw underlying exception on failure     // Caller's caller will also have to handle it     Execute v
try/catch: Problems Getting better, but… Now we’re using the try/catch exception mechanism for handling short-circuiting errors rather than real exception cases. Is the exception just due to a typical error in processing or is it a “real” exception? What does the caller do in this case? Note also that it becomes difficult for the caller to now be part of a larger workflow, or else a lot of hard-coded wireup The “inner workflows” called by the top-level workflow all need to have try/catch and also throw the same Exception type (e.g. MyProcessException).
Sequential Workflows: Extension Methods Using Extension Methods to “chain” or “pipeline” (in a C#/Java kind of way). The output of one function feeds the input of the next. Then, we wrap the whole thing in a try/catch. exception MyException of stringtype WrapperObject(v : 'T) =    let value = v    member x.Value with get() = vmodule WrapperObjectExtensions =    type WrapperObject with        member x.Process1() = let v = x.Value + " Process1" in WrapperObject(v)        member x.Process2() = let v = x.Value + " Process2" in WrapperObject(v)         member x.Process3() = let v = x.Value + " Process3" in WrapperObject(v) open WrapperObjectExtensionslet Execute (input : string) =    let wrapper = WrapperObject(input)    try        let res =  wrapper.Process1().Process2().Process3()        res.Value    with    | :? MyException as ex ->        // throw or return a Result?        reraise ()    | exn ->        // A "real" exception        // What to do here?        reraise ()
Sequential Workflows: Chained Objects Using Interfaces, we return instances of object, on which further Process() can be called. module ChainableObjectsWorkflowexception MyException of stringtype IChainableObject<'T> =    abstract Value : unit -> 'T with get    abstract Process : ('T -> 'T) -> IChainableObject<'T>type ChainableObject<'T>(v : 'T) as this =    let value = v    interface IChainableObject<'T> with        member x.Value with get() = value        override x.Process (f : ('T -> 'T)) =            let v = (this :> IChainableObject<_>).Value            let res = f v            ChainableObject(res) :> IChainableObject<'T>let process1 (s : string) = s + " Process1 applied"let process2 (s : string) = raise <| MyException("Error")let process3 (s : string) = s + " Process3 applied"
Sequential Workflows: Chained Objects (continued) Execute() function let Execute (input : string) =    let co = ChainableObject(input) :> IChainableObject<_>    try        let res = co.Process(process1).Process(process2).Process(process3)        res.Value    with    | :? MyException as ex ->        // throw or return a Result?        reraise ()    | exn ->        // A "real" exception        // What to do here?        reraise ()
Sequential Workflows: Pipelining Similar to Extension Methods but with more idiomatic F# syntax with (|>) instead of dot syntax exception MyException of string   let process1 input = input let process2 input = raise <| MyException("An error occurred") let process3 input = input   let Execute (input : 'T) =     try         input         |> process1         |> process2         |> process3     with     | :? MyException as ex ->         // throw or return a Result? reraise ()     | exn ->         // A "real" exception         // What to do here? reraise ()
Chaining, Pipelining, etc.: Problems Getting better, but… Still using the try/catch exception mechanism for handling short-circuiting errors rather than real exception cases. We just get the result of the overall computation, but not each individual piece. What if the workflow wants to perform additional processing on pieces? Once again, the 'T in Execute() and processX() is always the same
Help from Continuations module Continuationstype Result<'T> = | Success of 'T | Failure of stringlet process1 = (fun v -> Success("Process 1: " + v))let process2 = (fun v -> Failure("Process 2: An error occurred"))let process3 = (fun v -> Success("Process 3: " + v)) // Run f on v. If is succeeds, then call cont on that result, else return Failure // Note that cont can transform the result into another typelet executeCont v (f : 'a -> Result<'a>) (cont : 'a -> Result<'b>) : Result<'b> =    let maybe = f v     match maybe with     | Failure(err)    -> Failure(err)     | Success(result) -> cont result let Execute v : Result<_> =    executeCont v process1 (fun result1 ->        executeCont result1 process2 (fun result2 ->            executeCont result2 process3 (fun result3 -> Success(result3))))
Continuations Now we’re getting somewhere! Conditional computation – executeCont() can short-circuit We have access to intermediate results and could use these at any future point in the workflow The continuation function can transform the type from 'a to 'b. Now the types can be transformed in each stage of the workflow. More generic workflow helper functions (processX()) can be built which can manipulate different types. Still, ugly syntax. Could we improve on this?
A Better Way: F# Workflows First define a “Result” type which can be Success or Failure, plus some additional info Then define the “Monadic” type which wraps a type 'T into a function, which could be conditionally executed to return a Result Note that Attempt<'T> is basically a continuation. The Workflow Builder we create next contains the logic to run the continuation (the entire rest of the workflow) after running the current step, or else not run additional Attempts if there is a failure, and simply return out of the entire workflow type Error = { Message : string }/// A result/// If success, it contains some object, plus a message (perhaps a logging message)/// If failure, it returns an Error object (which could be expanded to be much richer)type Result<'T> =| Success of 'T * string| Failure of Errortype Attempt<'T> = (unit -> Result<'T>)
F# Workflow Builder: Helper functions let succeed (x,msg) = (fun () -> Success(x, msg)) : Attempt<'T>let fail err        = (fun () -> Failure(err)) : Attempt<'T>let failmsg msg     = (fun () -> Failure({ Message = msg })) : Attempt<'T>let runAttempt (a : Attempt<'T>) = a()let bind (f : Attempt<'T>) (rest : 'T -> Attempt<'U>) : Attempt<'U> =    match runAttempt f with    | Failure(msg)           -> fail msg    | Success(res, msg) as v -> rest reslet delay f = (fun () -> runAttempt (f()))let getValue (res:Result<'T>) = match res with    | Success(v,s) -> v    | Failure _ -> failwith "Invalid operation"
F# Workflow Builder: The Workflow Builder Object Uses the helper functions we just defined to create a “builder” class required by F# Creates “processor” which is an instance of the builder. This is used to wrap all of these workflows using processor { } notation Another “static class”, Processor, contains additional helper methods (kind of like the Async class) type ProcessBuilder() =    member this.Return(x) = succeed x    member this.ReturnFrom(x) = x    member this.Bind(p, rest) = bind p rest    member this.Delay(f) = delay f    member this.Let(p, rest) : Attempt<'T> = rest ptype Processor() =    static member Run workflow =        runAttempt workflow        let processor = new ProcessBuilder()
Mapping of Workflow Constructs
F# Workflow: Final Result See code for full example type Customer =     { Name : string; Birthdate : DateTime;  CreditScore : int; HasCriminalRecord : bool }let customerWorkflow c = processor {    let! ageTicket = processCustomer1 c    let! creditTicket = processCustomer2 c    let! criminalTicket = processCustomer3 c    // Process lots more stuff here...note how we can access result of each step    // If we didn't get to this point, then the entire workflow would have    // returned Result.Failure with the error message where the workflow failed    // If we got here, then all OK, assemble results and return    return ((c, [| ageTicket; creditTicket; criminalTicket |]), "Customer passed all checks!")    }/// If this succeeds, it returns a Result<Customer,int[]>/// else it returns a Failure with an error messagelet results = Processor.Run (customerWorkflow customer)
F# Workflows: Bind De-Sugared See code for full example let customer =     {    Name = "Jane Doe"; DateTime.Parse("1/1/1960"); CreditScore = 640; HasCriminalRecord = false }let customerWorkflow c logger = processor {    let! ageResult      = processCustomer1 (c, logger)    let! creditResult   = processCustomer2 (c, logger)     let! criminalResult = processCustomer3 (c, logger)     let ageTicket       = getValue(ageResult)    let creditTicket    = getValue(creditResult)    let criminalTicket  = getValue(criminalResult)    return ((c, [| ageTicket; creditTicket; criminalTicket |]),         "Customer passed all checks!", logger) } // De-sugars to: let finalResult =  processor.Bind(processCustomer1 c, (fun ageResult -> processor.Bind(processCustomer2 c, (fun creditResult -> processor.Bind(processCustomer3 c, (fun criminalResult -> processor.Let(getValue(ageResult), (fun ageTicket -> processor.Let(getValue(creditTicket), (fun creditTicket -> processor.Let(getValue(criminalResult), (fun criminalTicket -> processor.Return (c, [|ageTicket;creditTicket;criminalTicket|], logger                         ))))))))))
ParseWorkflow Example See example in code Complete example which parses and validates a fixed-width format specification and returns Line, Position and Message on any errors
Questions Questions? Thank you!
References Expert F# 2.0 (Don Syme, et al) Real World Functional Programming (Tomas Petricek with Jon Skeet) at http://www.manning.com/petricek/ Lots of F# and Haskell references Chance Coble “Why use Computation Workflows (aka Monads) in F#?” at http://leibnizdream.wordpress.com/2008/10/21/why-use-computation-workflows-aka-monads-in-f/ F# Survival Guide: Workflows at: http://www.ctocorner.com/fsharp/book/ch16.aspx DevHawk series: http://devhawk.net/CategoryView,category,Monads.aspx Understanding Haskell Monads (ErtugrulSöylemez) at http://ertes.de/articles/monads.html Monads are like Burritos: http://blog.plover.com/prog/burritos.html (and others) Many more

More Related Content

What's hot

Escalando aplicações no front-end com ReactJS
Escalando aplicações no front-end com ReactJSEscalando aplicações no front-end com ReactJS
Escalando aplicações no front-end com ReactJSCriciúma Dev
 
ASP.NET MVC Interview Questions and Answers by Shailendra Chauhan
ASP.NET MVC Interview Questions and Answers by Shailendra ChauhanASP.NET MVC Interview Questions and Answers by Shailendra Chauhan
ASP.NET MVC Interview Questions and Answers by Shailendra ChauhanShailendra Chauhan
 
Bloc Pattern - Practical Use Cases - Flutter London - 21JAN2019
Bloc Pattern - Practical Use Cases - Flutter London - 21JAN2019Bloc Pattern - Practical Use Cases - Flutter London - 21JAN2019
Bloc Pattern - Practical Use Cases - Flutter London - 21JAN2019Didier Boelens
 
Utilizing kotlin flows in an android application
Utilizing kotlin flows in an android applicationUtilizing kotlin flows in an android application
Utilizing kotlin flows in an android applicationSeven Peaks Speaks
 
Reactjs workshop (1)
Reactjs workshop (1)Reactjs workshop (1)
Reactjs workshop (1)Ahmed rebai
 
learn what React JS is & why we should use React JS .
learn what React JS is & why we should use React JS .learn what React JS is & why we should use React JS .
learn what React JS is & why we should use React JS .paradisetechsoftsolutions
 
Automated Frontend Testing
Automated Frontend TestingAutomated Frontend Testing
Automated Frontend TestingNeil Crosby
 
WCFのパイプ通信を .NET 5に向けてgRPCへ置き換える話
WCFのパイプ通信を .NET 5に向けてgRPCへ置き換える話WCFのパイプ通信を .NET 5に向けてgRPCへ置き換える話
WCFのパイプ通信を .NET 5に向けてgRPCへ置き換える話keitasudo1
 
Java EE 7 Batch processing in the Real World
Java EE 7 Batch processing in the Real WorldJava EE 7 Batch processing in the Real World
Java EE 7 Batch processing in the Real WorldRoberto Cortez
 
Email authentication using firebase auth + flutter
Email authentication using firebase auth + flutterEmail authentication using firebase auth + flutter
Email authentication using firebase auth + flutterKaty Slemon
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKirill Rozov
 
Web Connections & Services in FME: Tips & Tricks to Setting Up and Harnessing...
Web Connections & Services in FME: Tips & Tricks to Setting Up and Harnessing...Web Connections & Services in FME: Tips & Tricks to Setting Up and Harnessing...
Web Connections & Services in FME: Tips & Tricks to Setting Up and Harnessing...Safe Software
 
[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式
[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式
[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式Shengyou Fan
 
ARCHITECTURE MICROSERVICE : TOUR D’HORIZON DU CONCEPT ET BONNES PRATIQUES
ARCHITECTURE MICROSERVICE : TOUR D’HORIZON DU CONCEPT ET BONNES PRATIQUESARCHITECTURE MICROSERVICE : TOUR D’HORIZON DU CONCEPT ET BONNES PRATIQUES
ARCHITECTURE MICROSERVICE : TOUR D’HORIZON DU CONCEPT ET BONNES PRATIQUESSOAT
 
Integration Patterns and Anti-Patterns for Microservices Architectures
Integration Patterns and Anti-Patterns for Microservices ArchitecturesIntegration Patterns and Anti-Patterns for Microservices Architectures
Integration Patterns and Anti-Patterns for Microservices ArchitecturesApcera
 
Reactor 3.0, a reactive foundation for java 8 and Spring
Reactor 3.0, a reactive foundation for java 8 and SpringReactor 3.0, a reactive foundation for java 8 and Spring
Reactor 3.0, a reactive foundation for java 8 and SpringStéphane Maldini
 

What's hot (20)

Escalando aplicações no front-end com ReactJS
Escalando aplicações no front-end com ReactJSEscalando aplicações no front-end com ReactJS
Escalando aplicações no front-end com ReactJS
 
ASP.NET MVC Interview Questions and Answers by Shailendra Chauhan
ASP.NET MVC Interview Questions and Answers by Shailendra ChauhanASP.NET MVC Interview Questions and Answers by Shailendra Chauhan
ASP.NET MVC Interview Questions and Answers by Shailendra Chauhan
 
Bloc Pattern - Practical Use Cases - Flutter London - 21JAN2019
Bloc Pattern - Practical Use Cases - Flutter London - 21JAN2019Bloc Pattern - Practical Use Cases - Flutter London - 21JAN2019
Bloc Pattern - Practical Use Cases - Flutter London - 21JAN2019
 
Utilizing kotlin flows in an android application
Utilizing kotlin flows in an android applicationUtilizing kotlin flows in an android application
Utilizing kotlin flows in an android application
 
Dataweave
DataweaveDataweave
Dataweave
 
Reactjs workshop (1)
Reactjs workshop (1)Reactjs workshop (1)
Reactjs workshop (1)
 
learn what React JS is & why we should use React JS .
learn what React JS is & why we should use React JS .learn what React JS is & why we should use React JS .
learn what React JS is & why we should use React JS .
 
Automated Frontend Testing
Automated Frontend TestingAutomated Frontend Testing
Automated Frontend Testing
 
WCFのパイプ通信を .NET 5に向けてgRPCへ置き換える話
WCFのパイプ通信を .NET 5に向けてgRPCへ置き換える話WCFのパイプ通信を .NET 5に向けてgRPCへ置き換える話
WCFのパイプ通信を .NET 5に向けてgRPCへ置き換える話
 
Java EE 7 Batch processing in the Real World
Java EE 7 Batch processing in the Real WorldJava EE 7 Batch processing in the Real World
Java EE 7 Batch processing in the Real World
 
Roadmap to Development
Roadmap to DevelopmentRoadmap to Development
Roadmap to Development
 
C# Async Await
C# Async AwaitC# Async Await
C# Async Await
 
Email authentication using firebase auth + flutter
Email authentication using firebase auth + flutterEmail authentication using firebase auth + flutter
Email authentication using firebase auth + flutter
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is coming
 
Web Connections & Services in FME: Tips & Tricks to Setting Up and Harnessing...
Web Connections & Services in FME: Tips & Tricks to Setting Up and Harnessing...Web Connections & Services in FME: Tips & Tricks to Setting Up and Harnessing...
Web Connections & Services in FME: Tips & Tricks to Setting Up and Harnessing...
 
Accessing Mule variables in groovy
Accessing Mule variables in groovyAccessing Mule variables in groovy
Accessing Mule variables in groovy
 
[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式
[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式
[GDG Kaohsiung DevFest 2023] 以 Compose 及 Kotlin Multiplatform 打造多平台應用程式
 
ARCHITECTURE MICROSERVICE : TOUR D’HORIZON DU CONCEPT ET BONNES PRATIQUES
ARCHITECTURE MICROSERVICE : TOUR D’HORIZON DU CONCEPT ET BONNES PRATIQUESARCHITECTURE MICROSERVICE : TOUR D’HORIZON DU CONCEPT ET BONNES PRATIQUES
ARCHITECTURE MICROSERVICE : TOUR D’HORIZON DU CONCEPT ET BONNES PRATIQUES
 
Integration Patterns and Anti-Patterns for Microservices Architectures
Integration Patterns and Anti-Patterns for Microservices ArchitecturesIntegration Patterns and Anti-Patterns for Microservices Architectures
Integration Patterns and Anti-Patterns for Microservices Architectures
 
Reactor 3.0, a reactive foundation for java 8 and Spring
Reactor 3.0, a reactive foundation for java 8 and SpringReactor 3.0, a reactive foundation for java 8 and Spring
Reactor 3.0, a reactive foundation for java 8 and Spring
 

Similar to Understanding F# Workflows

Ecs 10 programming assignment 4 loopapalooza
Ecs 10 programming assignment 4   loopapaloozaEcs 10 programming assignment 4   loopapalooza
Ecs 10 programming assignment 4 loopapaloozaJenniferBall44
 
Macasu, gerrell c.
Macasu, gerrell c.Macasu, gerrell c.
Macasu, gerrell c.gerrell
 
Switch case and looping new
Switch case and looping newSwitch case and looping new
Switch case and looping newaprilyyy
 
The secret of Functional Programming revealed!
The secret of Functional Programming revealed!The secret of Functional Programming revealed!
The secret of Functional Programming revealed!Torbjørn Marø
 
maXbox Starter 36 Software Testing
maXbox Starter 36 Software TestingmaXbox Starter 36 Software Testing
maXbox Starter 36 Software TestingMax Kleiner
 
My programming final proj. (1)
My programming final proj. (1)My programming final proj. (1)
My programming final proj. (1)aeden_brines
 
c++ Data Types and Selection
c++ Data Types and Selectionc++ Data Types and Selection
c++ Data Types and SelectionAhmed Nobi
 
Switch case and looping kim
Switch case and looping kimSwitch case and looping kim
Switch case and looping kimkimberly_Bm10203
 
Switch case and looping
Switch case and loopingSwitch case and looping
Switch case and loopingChaAstillas
 
[4DEV] Bartosz Sokół - Functional developer in object oriented world - how F#...
[4DEV] Bartosz Sokół - Functional developer in object oriented world - how F#...[4DEV] Bartosz Sokół - Functional developer in object oriented world - how F#...
[4DEV] Bartosz Sokół - Functional developer in object oriented world - how F#...PROIDEA
 
maXbox Starter 31 Closures
maXbox Starter 31 ClosuresmaXbox Starter 31 Closures
maXbox Starter 31 ClosuresMax Kleiner
 
C++ Course - Lesson 1
C++ Course - Lesson 1C++ Course - Lesson 1
C++ Course - Lesson 1Mohamed Ahmed
 
Python programing
Python programingPython programing
Python programinghamzagame
 
parellel computing
parellel computingparellel computing
parellel computingkatakdound
 
Functional Programming in C# and F#
Functional Programming in C# and F#Functional Programming in C# and F#
Functional Programming in C# and F#Alfonso Garcia-Caro
 
Pseudocode-Flowchart
Pseudocode-FlowchartPseudocode-Flowchart
Pseudocode-Flowchartlotlot
 

Similar to Understanding F# Workflows (20)

Ecs 10 programming assignment 4 loopapalooza
Ecs 10 programming assignment 4   loopapaloozaEcs 10 programming assignment 4   loopapalooza
Ecs 10 programming assignment 4 loopapalooza
 
Macasu, gerrell c.
Macasu, gerrell c.Macasu, gerrell c.
Macasu, gerrell c.
 
Lecture 1
Lecture 1Lecture 1
Lecture 1
 
Lecture 1
Lecture 1Lecture 1
Lecture 1
 
Switch case and looping new
Switch case and looping newSwitch case and looping new
Switch case and looping new
 
Switch case and looping jam
Switch case and looping jamSwitch case and looping jam
Switch case and looping jam
 
My final requirement
My final requirementMy final requirement
My final requirement
 
The secret of Functional Programming revealed!
The secret of Functional Programming revealed!The secret of Functional Programming revealed!
The secret of Functional Programming revealed!
 
maXbox Starter 36 Software Testing
maXbox Starter 36 Software TestingmaXbox Starter 36 Software Testing
maXbox Starter 36 Software Testing
 
My programming final proj. (1)
My programming final proj. (1)My programming final proj. (1)
My programming final proj. (1)
 
c++ Data Types and Selection
c++ Data Types and Selectionc++ Data Types and Selection
c++ Data Types and Selection
 
Switch case and looping kim
Switch case and looping kimSwitch case and looping kim
Switch case and looping kim
 
Switch case and looping
Switch case and loopingSwitch case and looping
Switch case and looping
 
[4DEV] Bartosz Sokół - Functional developer in object oriented world - how F#...
[4DEV] Bartosz Sokół - Functional developer in object oriented world - how F#...[4DEV] Bartosz Sokół - Functional developer in object oriented world - how F#...
[4DEV] Bartosz Sokół - Functional developer in object oriented world - how F#...
 
maXbox Starter 31 Closures
maXbox Starter 31 ClosuresmaXbox Starter 31 Closures
maXbox Starter 31 Closures
 
C++ Course - Lesson 1
C++ Course - Lesson 1C++ Course - Lesson 1
C++ Course - Lesson 1
 
Python programing
Python programingPython programing
Python programing
 
parellel computing
parellel computingparellel computing
parellel computing
 
Functional Programming in C# and F#
Functional Programming in C# and F#Functional Programming in C# and F#
Functional Programming in C# and F#
 
Pseudocode-Flowchart
Pseudocode-FlowchartPseudocode-Flowchart
Pseudocode-Flowchart
 

Recently uploaded

ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITYISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITYKayeClaireEstoconing
 
Like-prefer-love -hate+verb+ing & silent letters & citizenship text.pdf
Like-prefer-love -hate+verb+ing & silent letters & citizenship text.pdfLike-prefer-love -hate+verb+ing & silent letters & citizenship text.pdf
Like-prefer-love -hate+verb+ing & silent letters & citizenship text.pdfMr Bounab Samir
 
Earth Day Presentation wow hello nice great
Earth Day Presentation wow hello nice greatEarth Day Presentation wow hello nice great
Earth Day Presentation wow hello nice greatYousafMalik24
 
How to do quick user assign in kanban in Odoo 17 ERP
How to do quick user assign in kanban in Odoo 17 ERPHow to do quick user assign in kanban in Odoo 17 ERP
How to do quick user assign in kanban in Odoo 17 ERPCeline George
 
Choosing the Right CBSE School A Comprehensive Guide for Parents
Choosing the Right CBSE School A Comprehensive Guide for ParentsChoosing the Right CBSE School A Comprehensive Guide for Parents
Choosing the Right CBSE School A Comprehensive Guide for Parentsnavabharathschool99
 
Inclusivity Essentials_ Creating Accessible Websites for Nonprofits .pdf
Inclusivity Essentials_ Creating Accessible Websites for Nonprofits .pdfInclusivity Essentials_ Creating Accessible Websites for Nonprofits .pdf
Inclusivity Essentials_ Creating Accessible Websites for Nonprofits .pdfTechSoup
 
4.18.24 Movement Legacies, Reflection, and Review.pptx
4.18.24 Movement Legacies, Reflection, and Review.pptx4.18.24 Movement Legacies, Reflection, and Review.pptx
4.18.24 Movement Legacies, Reflection, and Review.pptxmary850239
 
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTSGRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTSJoshuaGantuangco2
 
Science 7 Quarter 4 Module 2: Natural Resources.pptx
Science 7 Quarter 4 Module 2: Natural Resources.pptxScience 7 Quarter 4 Module 2: Natural Resources.pptx
Science 7 Quarter 4 Module 2: Natural Resources.pptxMaryGraceBautista27
 
AMERICAN LANGUAGE HUB_Level2_Student'sBook_Answerkey.pdf
AMERICAN LANGUAGE HUB_Level2_Student'sBook_Answerkey.pdfAMERICAN LANGUAGE HUB_Level2_Student'sBook_Answerkey.pdf
AMERICAN LANGUAGE HUB_Level2_Student'sBook_Answerkey.pdfphamnguyenenglishnb
 
4.16.24 21st Century Movements for Black Lives.pptx
4.16.24 21st Century Movements for Black Lives.pptx4.16.24 21st Century Movements for Black Lives.pptx
4.16.24 21st Century Movements for Black Lives.pptxmary850239
 
Procuring digital preservation CAN be quick and painless with our new dynamic...
Procuring digital preservation CAN be quick and painless with our new dynamic...Procuring digital preservation CAN be quick and painless with our new dynamic...
Procuring digital preservation CAN be quick and painless with our new dynamic...Jisc
 
How to Add Barcode on PDF Report in Odoo 17
How to Add Barcode on PDF Report in Odoo 17How to Add Barcode on PDF Report in Odoo 17
How to Add Barcode on PDF Report in Odoo 17Celine George
 
Difference Between Search & Browse Methods in Odoo 17
Difference Between Search & Browse Methods in Odoo 17Difference Between Search & Browse Methods in Odoo 17
Difference Between Search & Browse Methods in Odoo 17Celine George
 
FILIPINO PSYCHology sikolohiyang pilipino
FILIPINO PSYCHology sikolohiyang pilipinoFILIPINO PSYCHology sikolohiyang pilipino
FILIPINO PSYCHology sikolohiyang pilipinojohnmickonozaleda
 
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATIONTHEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATIONHumphrey A Beña
 
Student Profile Sample - We help schools to connect the data they have, with ...
Student Profile Sample - We help schools to connect the data they have, with ...Student Profile Sample - We help schools to connect the data they have, with ...
Student Profile Sample - We help schools to connect the data they have, with ...Seán Kennedy
 

Recently uploaded (20)

ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITYISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
 
Like-prefer-love -hate+verb+ing & silent letters & citizenship text.pdf
Like-prefer-love -hate+verb+ing & silent letters & citizenship text.pdfLike-prefer-love -hate+verb+ing & silent letters & citizenship text.pdf
Like-prefer-love -hate+verb+ing & silent letters & citizenship text.pdf
 
Earth Day Presentation wow hello nice great
Earth Day Presentation wow hello nice greatEarth Day Presentation wow hello nice great
Earth Day Presentation wow hello nice great
 
How to do quick user assign in kanban in Odoo 17 ERP
How to do quick user assign in kanban in Odoo 17 ERPHow to do quick user assign in kanban in Odoo 17 ERP
How to do quick user assign in kanban in Odoo 17 ERP
 
Choosing the Right CBSE School A Comprehensive Guide for Parents
Choosing the Right CBSE School A Comprehensive Guide for ParentsChoosing the Right CBSE School A Comprehensive Guide for Parents
Choosing the Right CBSE School A Comprehensive Guide for Parents
 
Inclusivity Essentials_ Creating Accessible Websites for Nonprofits .pdf
Inclusivity Essentials_ Creating Accessible Websites for Nonprofits .pdfInclusivity Essentials_ Creating Accessible Websites for Nonprofits .pdf
Inclusivity Essentials_ Creating Accessible Websites for Nonprofits .pdf
 
4.18.24 Movement Legacies, Reflection, and Review.pptx
4.18.24 Movement Legacies, Reflection, and Review.pptx4.18.24 Movement Legacies, Reflection, and Review.pptx
4.18.24 Movement Legacies, Reflection, and Review.pptx
 
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTSGRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
 
Science 7 Quarter 4 Module 2: Natural Resources.pptx
Science 7 Quarter 4 Module 2: Natural Resources.pptxScience 7 Quarter 4 Module 2: Natural Resources.pptx
Science 7 Quarter 4 Module 2: Natural Resources.pptx
 
AMERICAN LANGUAGE HUB_Level2_Student'sBook_Answerkey.pdf
AMERICAN LANGUAGE HUB_Level2_Student'sBook_Answerkey.pdfAMERICAN LANGUAGE HUB_Level2_Student'sBook_Answerkey.pdf
AMERICAN LANGUAGE HUB_Level2_Student'sBook_Answerkey.pdf
 
4.16.24 21st Century Movements for Black Lives.pptx
4.16.24 21st Century Movements for Black Lives.pptx4.16.24 21st Century Movements for Black Lives.pptx
4.16.24 21st Century Movements for Black Lives.pptx
 
Procuring digital preservation CAN be quick and painless with our new dynamic...
Procuring digital preservation CAN be quick and painless with our new dynamic...Procuring digital preservation CAN be quick and painless with our new dynamic...
Procuring digital preservation CAN be quick and painless with our new dynamic...
 
How to Add Barcode on PDF Report in Odoo 17
How to Add Barcode on PDF Report in Odoo 17How to Add Barcode on PDF Report in Odoo 17
How to Add Barcode on PDF Report in Odoo 17
 
Difference Between Search & Browse Methods in Odoo 17
Difference Between Search & Browse Methods in Odoo 17Difference Between Search & Browse Methods in Odoo 17
Difference Between Search & Browse Methods in Odoo 17
 
YOUVE_GOT_EMAIL_PRELIMS_EL_DORADO_2024.pptx
YOUVE_GOT_EMAIL_PRELIMS_EL_DORADO_2024.pptxYOUVE_GOT_EMAIL_PRELIMS_EL_DORADO_2024.pptx
YOUVE_GOT_EMAIL_PRELIMS_EL_DORADO_2024.pptx
 
FILIPINO PSYCHology sikolohiyang pilipino
FILIPINO PSYCHology sikolohiyang pilipinoFILIPINO PSYCHology sikolohiyang pilipino
FILIPINO PSYCHology sikolohiyang pilipino
 
FINALS_OF_LEFT_ON_C'N_EL_DORADO_2024.pptx
FINALS_OF_LEFT_ON_C'N_EL_DORADO_2024.pptxFINALS_OF_LEFT_ON_C'N_EL_DORADO_2024.pptx
FINALS_OF_LEFT_ON_C'N_EL_DORADO_2024.pptx
 
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATIONTHEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
 
Raw materials used in Herbal Cosmetics.pptx
Raw materials used in Herbal Cosmetics.pptxRaw materials used in Herbal Cosmetics.pptx
Raw materials used in Herbal Cosmetics.pptx
 
Student Profile Sample - We help schools to connect the data they have, with ...
Student Profile Sample - We help schools to connect the data they have, with ...Student Profile Sample - We help schools to connect the data they have, with ...
Student Profile Sample - We help schools to connect the data they have, with ...
 

Understanding F# Workflows

  • 1. Understanding F# Workflows New England F# User’s Group Presentation (fsug.org) August 2, 2010 Scott Theleman
  • 2. Overview F# Workflows – closely related to Monads in Haskell and other languages – are a powerful and elegant tool for solving many real-world problems, though they can be rather daunting at first. We'll survey some ways in which Workflows in the standard F# libraries are used for common development tasks, then dig into detail on how they work. Finally we’ll build a workflow that provides a validation framework that can be used for parsing or other tasks.
  • 3. Intro to Monads A “Monad” is generally a set of interrelated constructs. At the least, it usually consists of: A “Monadic Type” A bind function (sometimes the (>>=) operator is used) A return function “When the designers of F# talked with the designers of Haskell about this, they agreed that the word monad is obscure and sounds a little daunting and that using other names might be wise” — Expert F# 2.0 (Don Syme, Adam Granicz, Antonio Cisternino)
  • 4. Characteristics of Monads The Monadic Type “wraps” an underlying type. The monadic type may be more like an object (which may contain other data or state), or more like a computation or potential computation. The Return function wraps an underlying type in a monadic type. The Bind function takes an underlying type as well as a function which maps from the underlying type to a new monadic type, and returns a new monadic type. By performing this wrapping of underlying types inside a monadic type and providing bind and return, you can now combine computations of that inner type in ways that are difficult or impossible when just dealing with the underlying types.
  • 6. Uses of Monads aka Workflows
  • 7. One use of Monads: Sequential Workflows As noted, there are many uses and varieties of Monads We will concentrate on solving a typical sequential workflow style problem First showing other ways this has been done without workflows, then building up to using an F# workflow
  • 8. Sequential Workflows: If/else The following code takes an initial input (of type T) and performs 3 sets of transformations on it, each time returning a tuple of bool and Result object (of type T). If there is a failure at any step, the entire operation is short circuited. let process1 = true, input // do something with input let process2 = false, input let process3 = true, input   let Execute (input : 'T) = let ok, result1 = process1 input if ok then let ok, result2 = process2 result1 if ok then let ok, result3 = process3 result2 ok, result3 else false, result2 else false, result1
  • 9. If/else: Problems The processX() methods and their callers all must know about the input and result types. Generics help the situation, but still these methods are hard-wired for those specific types, plus the success/failure Boolean. Also, the 'T in Execute() and processX() is always the same! It’s getting pretty messy, and we’ve only done 3 transformations. Pretty soon the code is going to be off the right side of the screen! We have to explicitly handle failure at every step of the process Lots of redundancy. We said “ok” 6 times! We don’t have any information about what went wrong. Though we could define some sort of error type (see next example…).
  • 10. Sequential Workflows: Option and match The following code tries to improve on the last sample. It now includes a Result<'T> type which we could expand upon to return detailed error information. It also uses pattern matching, which makes the code a bit clearer. type Result<'T> = | Success of 'T | Failure of string   let process1 input = Success(input) // do something interesting here let process2 input = Failure("Some error") let process3 input = Success(input)    let Process (input : 'T) = let res1 = process1 input match res1 with | Failure _ -> res1 | Success v -> let res2 = process2 v match res2 with | Failure _ -> res2 | Success v -> let res3 = process3 v res3
  • 11. Option/match: Problems Better than if/else, but… Still messy and redundant and again the code is drifting off the right side of the screen The processX() methods and their callers still must all know about the input and result types. The 'T in Execute() and processX() is still always the same We still have to explicitly handle failure at every step of the process The Result<'T>type does seem like a nice idea
  • 12. Sequential Workflows: try/catch Try/catch could simplify/aggregate and improve things a bit – though just for this particular case. It does look nice and streamlined, which is one thing we are looking for. exception MyProcessException of string   let process1 input = input let process2 input = raise <| MyProcessException("An error occurred“) let process3 input = input   // processX now accept and return T // No Result any more; exceptions are used instead let Execute (input : 'T) = try let v1 = process1 input let v2 = process2 v1 let v3 = process3 v2 v3 with | :? MyProcessException as ex -> // Catch application-specific error...do we throw or return a Result?? reraise () | exn -> // A "real" exception...what to do here? reraise ()   let Caller<'T> v = // This will throw underlying exception on failure // Caller's caller will also have to handle it Execute v
  • 13. try/catch: Problems Getting better, but… Now we’re using the try/catch exception mechanism for handling short-circuiting errors rather than real exception cases. Is the exception just due to a typical error in processing or is it a “real” exception? What does the caller do in this case? Note also that it becomes difficult for the caller to now be part of a larger workflow, or else a lot of hard-coded wireup The “inner workflows” called by the top-level workflow all need to have try/catch and also throw the same Exception type (e.g. MyProcessException).
  • 14. Sequential Workflows: Extension Methods Using Extension Methods to “chain” or “pipeline” (in a C#/Java kind of way). The output of one function feeds the input of the next. Then, we wrap the whole thing in a try/catch. exception MyException of stringtype WrapperObject(v : 'T) =    let value = v    member x.Value with get() = vmodule WrapperObjectExtensions =    type WrapperObject with        member x.Process1() = let v = x.Value + " Process1" in WrapperObject(v)        member x.Process2() = let v = x.Value + " Process2" in WrapperObject(v)         member x.Process3() = let v = x.Value + " Process3" in WrapperObject(v) open WrapperObjectExtensionslet Execute (input : string) =    let wrapper = WrapperObject(input)    try        let res =  wrapper.Process1().Process2().Process3()        res.Value    with    | :? MyException as ex ->        // throw or return a Result?        reraise ()    | exn ->        // A "real" exception        // What to do here?        reraise ()
  • 15. Sequential Workflows: Chained Objects Using Interfaces, we return instances of object, on which further Process() can be called. module ChainableObjectsWorkflowexception MyException of stringtype IChainableObject<'T> =    abstract Value : unit -> 'T with get    abstract Process : ('T -> 'T) -> IChainableObject<'T>type ChainableObject<'T>(v : 'T) as this =    let value = v    interface IChainableObject<'T> with        member x.Value with get() = value        override x.Process (f : ('T -> 'T)) =            let v = (this :> IChainableObject<_>).Value            let res = f v            ChainableObject(res) :> IChainableObject<'T>let process1 (s : string) = s + " Process1 applied"let process2 (s : string) = raise <| MyException("Error")let process3 (s : string) = s + " Process3 applied"
  • 16. Sequential Workflows: Chained Objects (continued) Execute() function let Execute (input : string) =    let co = ChainableObject(input) :> IChainableObject<_>    try        let res = co.Process(process1).Process(process2).Process(process3)        res.Value    with    | :? MyException as ex ->        // throw or return a Result?        reraise ()    | exn ->        // A "real" exception        // What to do here?        reraise ()
  • 17. Sequential Workflows: Pipelining Similar to Extension Methods but with more idiomatic F# syntax with (|>) instead of dot syntax exception MyException of string   let process1 input = input let process2 input = raise <| MyException("An error occurred") let process3 input = input   let Execute (input : 'T) = try input |> process1 |> process2 |> process3 with | :? MyException as ex -> // throw or return a Result? reraise () | exn -> // A "real" exception // What to do here? reraise ()
  • 18. Chaining, Pipelining, etc.: Problems Getting better, but… Still using the try/catch exception mechanism for handling short-circuiting errors rather than real exception cases. We just get the result of the overall computation, but not each individual piece. What if the workflow wants to perform additional processing on pieces? Once again, the 'T in Execute() and processX() is always the same
  • 19. Help from Continuations module Continuationstype Result<'T> = | Success of 'T | Failure of stringlet process1 = (fun v -> Success("Process 1: " + v))let process2 = (fun v -> Failure("Process 2: An error occurred"))let process3 = (fun v -> Success("Process 3: " + v)) // Run f on v. If is succeeds, then call cont on that result, else return Failure // Note that cont can transform the result into another typelet executeCont v (f : 'a -> Result<'a>) (cont : 'a -> Result<'b>) : Result<'b> = let maybe = f v match maybe with | Failure(err) -> Failure(err) | Success(result) -> cont result let Execute v : Result<_> =    executeCont v process1 (fun result1 ->        executeCont result1 process2 (fun result2 ->            executeCont result2 process3 (fun result3 -> Success(result3))))
  • 20. Continuations Now we’re getting somewhere! Conditional computation – executeCont() can short-circuit We have access to intermediate results and could use these at any future point in the workflow The continuation function can transform the type from 'a to 'b. Now the types can be transformed in each stage of the workflow. More generic workflow helper functions (processX()) can be built which can manipulate different types. Still, ugly syntax. Could we improve on this?
  • 21. A Better Way: F# Workflows First define a “Result” type which can be Success or Failure, plus some additional info Then define the “Monadic” type which wraps a type 'T into a function, which could be conditionally executed to return a Result Note that Attempt<'T> is basically a continuation. The Workflow Builder we create next contains the logic to run the continuation (the entire rest of the workflow) after running the current step, or else not run additional Attempts if there is a failure, and simply return out of the entire workflow type Error = { Message : string }/// A result/// If success, it contains some object, plus a message (perhaps a logging message)/// If failure, it returns an Error object (which could be expanded to be much richer)type Result<'T> =| Success of 'T * string| Failure of Errortype Attempt<'T> = (unit -> Result<'T>)
  • 22. F# Workflow Builder: Helper functions let succeed (x,msg) = (fun () -> Success(x, msg)) : Attempt<'T>let fail err        = (fun () -> Failure(err)) : Attempt<'T>let failmsg msg     = (fun () -> Failure({ Message = msg })) : Attempt<'T>let runAttempt (a : Attempt<'T>) = a()let bind (f : Attempt<'T>) (rest : 'T -> Attempt<'U>) : Attempt<'U> =    match runAttempt f with    | Failure(msg)           -> fail msg    | Success(res, msg) as v -> rest reslet delay f = (fun () -> runAttempt (f()))let getValue (res:Result<'T>) = match res with    | Success(v,s) -> v    | Failure _ -> failwith "Invalid operation"
  • 23. F# Workflow Builder: The Workflow Builder Object Uses the helper functions we just defined to create a “builder” class required by F# Creates “processor” which is an instance of the builder. This is used to wrap all of these workflows using processor { } notation Another “static class”, Processor, contains additional helper methods (kind of like the Async class) type ProcessBuilder() =    member this.Return(x) = succeed x    member this.ReturnFrom(x) = x    member this.Bind(p, rest) = bind p rest    member this.Delay(f) = delay f    member this.Let(p, rest) : Attempt<'T> = rest ptype Processor() =    static member Run workflow =        runAttempt workflow        let processor = new ProcessBuilder()
  • 24. Mapping of Workflow Constructs
  • 25. F# Workflow: Final Result See code for full example type Customer = { Name : string; Birthdate : DateTime;  CreditScore : int; HasCriminalRecord : bool }let customerWorkflow c = processor {    let! ageTicket = processCustomer1 c    let! creditTicket = processCustomer2 c    let! criminalTicket = processCustomer3 c    // Process lots more stuff here...note how we can access result of each step    // If we didn't get to this point, then the entire workflow would have    // returned Result.Failure with the error message where the workflow failed    // If we got here, then all OK, assemble results and return    return ((c, [| ageTicket; creditTicket; criminalTicket |]), "Customer passed all checks!")    }/// If this succeeds, it returns a Result<Customer,int[]>/// else it returns a Failure with an error messagelet results = Processor.Run (customerWorkflow customer)
  • 26. F# Workflows: Bind De-Sugared See code for full example let customer = { Name = "Jane Doe"; DateTime.Parse("1/1/1960"); CreditScore = 640; HasCriminalRecord = false }let customerWorkflow c logger = processor {    let! ageResult  = processCustomer1 (c, logger)    let! creditResult  = processCustomer2 (c, logger)     let! criminalResult = processCustomer3 (c, logger) let ageTicket = getValue(ageResult)    let creditTicket  = getValue(creditResult)    let criminalTicket  = getValue(criminalResult)    return ((c, [| ageTicket; creditTicket; criminalTicket |]), "Customer passed all checks!", logger) } // De-sugars to: let finalResult = processor.Bind(processCustomer1 c, (fun ageResult -> processor.Bind(processCustomer2 c, (fun creditResult -> processor.Bind(processCustomer3 c, (fun criminalResult -> processor.Let(getValue(ageResult), (fun ageTicket -> processor.Let(getValue(creditTicket), (fun creditTicket -> processor.Let(getValue(criminalResult), (fun criminalTicket -> processor.Return (c, [|ageTicket;creditTicket;criminalTicket|], logger ))))))))))
  • 27. ParseWorkflow Example See example in code Complete example which parses and validates a fixed-width format specification and returns Line, Position and Message on any errors
  • 29. References Expert F# 2.0 (Don Syme, et al) Real World Functional Programming (Tomas Petricek with Jon Skeet) at http://www.manning.com/petricek/ Lots of F# and Haskell references Chance Coble “Why use Computation Workflows (aka Monads) in F#?” at http://leibnizdream.wordpress.com/2008/10/21/why-use-computation-workflows-aka-monads-in-f/ F# Survival Guide: Workflows at: http://www.ctocorner.com/fsharp/book/ch16.aspx DevHawk series: http://devhawk.net/CategoryView,category,Monads.aspx Understanding Haskell Monads (ErtugrulSöylemez) at http://ertes.de/articles/monads.html Monads are like Burritos: http://blog.plover.com/prog/burritos.html (and others) Many more