Click here to Skip to main content
15,878,959 members
Articles / Desktop Programming / Win32

An Idea of F# Workflow Engine based on Concurrency and Coordination Runtime

Rate me:
Please Sign up or sign in to vote.
4.82/5 (8 votes)
20 Feb 2009CPOL3 min read 32.8K   20   7
This article describes the basic ideas of how to build workflow engine a-la WF based on F# workflows and CCR

Introduction

This article describes basic ideas of how to build workflow engine a-la Workflow Foundation based on F# workflows and CCR.

Main Idea

Recently I've played a bit with F# workflows feature and one crazy idea appeared in my fevered imagination: “Would it be possible to build a workflow engine a-la Windows Workflow Foundation using F# workflows?”. In this article, you can see an attempt to build such an engine, at least a very initial version of it. First of all, I would like to provide you with some useful links for those who haven't had a chance to play with F# and F# workflows in particular:

So, if you continue reading this, then you probably already have some F# knowledge or have checked the links above… or just want to check what this crazy guy is talking about.

Let’s start with a small sample:

let workflowHost = new WorkflowEngine()

let fibonacciWorkflow number = 
    workflowHost {
                do printfn "Started!"
                let! f1 = fib number
                let! f2 = fib (number + 1)
                do printfn "Finished"
                do printfn "Result is %s" ((f1 + f2).ToString())
                return 0 //success
     }

runWorkflow(fibonacciWorkflow(22));;  

What do we have here: A workflow or THE workflow called fibonacciWorkflow. Something very trivial: we calculate two Fibonacci numbers and print out the sum of these numbers. Here fib function calculates Fibonacci number for a given position. Honestly, I've copy pasted this function from somewhere on the web. :)

let fib n =
    let rec fib_aux (n, a, b) =
        match (n, a, b) with
          | (0, a, b) -> a
          | _ -> fib_aux (n - 1, a + b, a)
    fun () -> fib_aux (n, 0, 1)		 

Looks like nothing special - a very trivial workflow. Let's have some fun now - it's time to have a closer look at the workflow engine.

Of course, each dependable workflow engine should manage threading and execute all workflows and activities in parallel (or not?) threads where possible and tries to do its best to increase performance. So our engine tries to follow the tradition and uses CCR to execute all activities (activities you can identify by “let!” keyword). Here we have two activities and each one is transformed behind the scene to CCR task. The output of each activity (CCR task) is taken as an input to the next activity (CCR task).

Before I show the actual implementation, I'd like to describe one more feature of our workflow engine.

The second thing each dependable workflow engine should do is to provide developers with the ability to inject aspects during activity execution. Let’s define trivial security aspect which is intended to notify someone (probably a system administrator) about a security alert (let's assume that in your company it is forbidden to calculate Fibonacci numbers :)) :)

let securityAspect() =
    printfn "Security Alert: Wake up, someone is running your workflow!!!"

This is a very, very simple aspect. I could make it more complicated (for instance, validate activity input within aspect)… maybe next time. :)

Now it’s time to have a look at the workflow engine code (by the way, please forgive me for using “engine” term here, in F# usually is used workflow builder term): 
type Activity<'a> = unit -> 'a

let runWorkflow (f:Activity<'a>) = f()

type WorkflowEngine() =
    let dispatcher = new Dispatcher(8, "MyDispatcher") //from Microsoft.Ccr.Core
    let queue = new DispatcherQueue("MyQueue", dispatcher) // from Microsoft.Ccr.Core
    
    member b.Let(p, rest) =
        rest p
    member b.Return(x) = 
        fun () -> x
    member b.Delay f = 
        fun () -> (runWorkflow f)()
    member b.Bind(p, rest) = 
        securityAspect()
        fun () -> 
            let res = queue.Enqueue(new Task( 
                                        fun () -> 
                                            printfn "Thread id is: %d" 
					Thread.CurrentThread.ManagedThreadId
                                            let res = p()
                                            let b = queue.Enqueue(new Task(
                                                                        fun () ->
                                                                           let fakeVal =
								    rest(res)()
                                                                            ()
                                                ))
                                            ()
                                        ))
            new 'b()//return fake value - just to fool the F# compiler :)... sorry, 
	           //it's ugly, but I haven't yet found a better way

Each keyword in the workflow is transformed to a method in the workflow builder: let -> Let, let! -> Bind, return -> Return and Delay method is used to provide invocation point for the whole workflow. Actually, all workflow-engine-features-I-have-stated-above-related functionality is implemented within the Bind method. It is possible to define additional keywords, like for, while, use, if/then… etc. Here I've implemented only the basic set of all possible keywords.

The important point here is that with the help of F# compiler, you have reasonable level of control over each line of code within the workflow and can influence it with aspects or any other ways you want.

The other important point is that within workflow, you can keep state of workflow execution.

Conclusion

Here I presented the basic ideas of how to build a workflow engine based on F# workflows. The engine provided above is pretty trivial and its mission is to convey a basic idea of possible implementation. There is absolutely no support for centralized exception handling (dependable workflow engine should also provide it), which could be achieved through output ports in CCR. And CCR usage is pretty superficial, I use it in a very simple and dummy way. You can find much better CCR-related articles if you want to learn it (I've used some CCR samples as a base).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead bwin Interactive Entertainment AG
Austria Austria
The views expressed in my articles are mine and do not necessarily reflect the views of my employer.

if(youWantToContactMe)
{
SendMessage(string.Format("{0}@{1}.com", "liptchinski_vit", "yahoo"));
}

More info in my LinkedIn profile:
http://www.linkedin.com/in/vitaliyliptchinsky

Comments and Discussions

 
GeneralMy vote of 5 Pin
Art Tres8-Jul-13 7:58
Art Tres8-Jul-13 7:58 
QuestionMessage Closed Pin
20-Jun-11 10:34
wijayd20-Jun-11 10:34 
General[My vote of 2] Errors & Questions [modified] Pin
Bourlesque9-Aug-10 16:18
Bourlesque9-Aug-10 16:18 
GeneralRe: [My vote of 2] Errors & Questions Pin
Vitaliy Liptchinsky16-Aug-10 2:07
Vitaliy Liptchinsky16-Aug-10 2:07 
AnswerRe: [My vote of 2] Errors & Questions [modified] Pin
Bourlesque17-Aug-10 12:54
Bourlesque17-Aug-10 12:54 
---------------------------
RE2Errors:

---------------------------
1. Let's suppose that it's true. Then explain please what are you doing in the folowing line:
do printfn "Result is %s" ((f1 + f2).ToString())

You said fib was a factory, then what for are you summing the 'factories': f1 + f2? What do you expect from the code: (f1 + f2).ToString() ? A number or a reflection information about the new 'factory'?
---------------------------
2. The demonstration has completely failed. If you believe that 'workflow itself is meaningless', then what was the reason to write the article named 'An idea of F# Workflow...'?
---------------------------
3. Falsism. All known books about F# were written before the official release of 2010, but they are still actual.
---------------------------

---------------------------
RE2Answers:

---------------------------
1. Yes, workflow in F# is a monad in terms of Haskell. Your article did not reveal the advantages of workflows against traditional Object Oriented approaches. Having made some much mistakes in your article you achieved an opposite effect.
---------------------------
2. Again falsity. Sound proofless.
---------------------------

modified on Tuesday, August 17, 2010 7:10 PM

GeneralRe: [My vote of 2] Errors & Questions Pin
Vitaliy Liptchinsky17-Aug-10 21:37
Vitaliy Liptchinsky17-Aug-10 21:37 
AnswerRe: [My vote of 2] Errors & Questions Pin
Bourlesque18-Aug-10 4:37
Bourlesque18-Aug-10 4:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.