12,885,242 members (43,041 online)
Add your own
alternative version

#### Stats

22K views
20 bookmarked
Posted 20 Feb 2009

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

, 20 Feb 2009 CPOL
 Rate this:
Please Sign up or sign in to vote.
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)

## About the Author

 Technical Lead bwin Interactive Entertainment AG 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

 Pro

## Comments and Discussions

 View All Threads First Prev Next
 [My vote of 2] Errors & Questions [modified] Member 1984809-Aug-10 16:18 Member 198480 9-Aug-10 16:18
 I. Errors 1. The fib function returns a function (`unit->int`), but should return (`int`). The correction: ```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) fib_aux (n, 0, 1) // !!! fun () -> fib_aux (n, 0, 1)``` 2. The `fibonacciWorkflow` is not really a fibonacci workflow for `number`. It tries to do something with two fibonacci numbers. Why `f2`, if `f1` is already what you need? ```... workflowHost { do printfn "Started!" let! f1 = fib number let! f2 = fib (number + 1) // What for?! ... ``` 3a. Unused lambda expression: `fun () -> something()` If you need to make a call of something, you should not write "`fun () -> something()`", you may just write "`something()`" ```member b.Bind(p, rest) = securityAspect() fun () -> something() // It won't be invoked! ``` Correction: ```member b.Bind(p, rest) = securityAspect() something() ``` 3b. Again unused lambda expression: ```new Task( fun () -> let fakeVal = rest(res)() // It won't be invoked ! (Even compiled...) () // This is an epmty body of the lambda expression passed as a parameter into the Task's constructor``` Correction: ```new Task (fun () -> rest(res) |> ignore; ()) ``` II. Questions 1. What the need of Workflows if a task can be very easily solved in traditional OOP fashion (via class with incapsulated functionality in it)? 2. The code even cannot be compiled. Did you try? What for this article if the code is so wrong? modified on Tuesday, August 10, 2010 9:55 AM
 Re: [My vote of 2] Errors & Questions Vitaliy Liptchinsky16-Aug-10 2:07 Vitaliy Liptchinsky 16-Aug-10 2:07
 Re: [My vote of 2] Errors & Questions [modified] Bourlesque17-Aug-10 12:54 Bourlesque 17-Aug-10 12:54
 Re: [My vote of 2] Errors & Questions Vitaliy Liptchinsky17-Aug-10 21:37 Vitaliy Liptchinsky 17-Aug-10 21:37
 Re: [My vote of 2] Errors & Questions Bourlesque18-Aug-10 4:37 Bourlesque 18-Aug-10 4:37
 Last Visit: 31-Dec-99 18:00     Last Update: 24-Apr-17 19:34 Refresh 1

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    Admin

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

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170424.1 | Last Updated 20 Feb 2009
Article Copyright 2009 by Vitaliy Liptchinsky
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid