...collaborate on

The HOrc Primer

Orc is an academic language, designed to express coordination patterns that are frequent in distributed systems, namely those that are based on web-services.

HOrc provides a concrete realization of Orc that can be easily used for:

  • Educational purposes
  • Prototyping
  • Verification of Systems

Architecturally, HOrc is based around the language Haskell and its standard libraries. In fact, every HOrc program is a Haskell program; this is because all the operators of Orc have been implemented as Haskell functions, packed into a (combinator) library.

Creating Sites

The basic building blocks of any Orc (and hence HOrc) expression are site-calls. They are the ones that compute relevant values and, thus, the ones that we must coordinate. The simples example of a "thing" which computes a value is a pure function, like Haskell's; moving up the ladder we also find functions with side-effects and remote procedure calls. These kinds of computations can all be represented in Haskell by so called IO values.

Sadly, due to the way HOrc works internally, it isn't possible to yse these functions directly in HOrc programs -- there is some housekeeping that needs to be done and that cannot be avoided.

But worry not! For every every such computation, we can create a site that is runnable by HOrc using the function

liftIO :: IO a -> Action a

As you can infer from the type signature, Action a is the type of every site that compute a value of type -- in fact, of every HOrc expression that returns (or publishes) values of type a. And when we say "returns values" we mean it: a HOrc expression may publish several values in one invocation (unlike functions).

Site calls may fail, terminating without returning any results. For those situations, the function above is not good enough, as it assumes termination; instead, one should use

liftMaybe :: IO (Maybe a) -> Action a

Every time the IO value of the argument is Nothing, the site returned by this operator does not publish any result; it is a Just a, then it publishes the a.

Finally, in the most general case, the site may publish several values. We can create one such site with

liftList :: IO [a] -> Action a

For every value in the list returned by the computation, the resulting site will make a publication with that value.

Enough yapping and on to some concrete examples! Orc includes several sites that have privileged running properties; it was impossible to retain those privileges in HOrc. But their behavior can be reproduced using the functions above.

TODO: add simple sites here.

Running Orchestrations in Parallel

Now that we can do computations, it is time to coordinate them so that they can exhibit more complex behavior. The simplest operator we have is

mplus :: Action a -> Action a -> Action a

it receives two orchestrations and runs them in parallels. The resulting orchestration publishes the values that are published by the sub-orchestrations; for this reason, they need to have the same type. There is no imposition on which order they are run. E.g.

oprint "a" `mplus` oprint "b"

may print either the 'a' or 'b' first.

Running Orchestrations in a Sequence I

What good would be the values that we published if they couldn't be used to influence the orchestration? To that end we make use of the operator

>>= :: Action a -> (a -> Action b) -> Action b

The result of this operator is an orchestration that runs the first argument, and passes the values published by it to the second orchestration; the way this is done is by having the second orchestration represented by a function whose variable will receive the published values. The second orchestration is then run and its publications forwarded to the outside world. Note, however, that an orchestration may publish several results; for each value published by the first orchestration, the operator creates a new instance of the second and runs it in parallel with the others. We could have, thus, written the previous example as

liftList ["a","b"] >>= \s -> oprint s

to much the same effect. (Of course, we could have omitted variable "s", but it makes clear that the right-hand side of the expression must be a function.)

Intermezzo: Monads

People from the FP community will no doubt find the name of the operators telling. They tell that orchestrations are monads. Monads have a not always stelar reputation among programers. The details are not important for our purposes -- though they can be found on the papers found in the Documentation. What is important is that orchestrations belong to the MonadPlus class and that means orchestrations can reuse the many, many functions defined on Haskell's Control.Monad module

Running Orchestrations in a Sequence II

The final operator works when we don't want all the publications of an orchestration but only the first.

prune :: (a -> Action b) -> Action a -> Action b

it works basically the same as the sequencing operator above with the arguments fliped; however, compared to the latter, this operator removes some of the branches of the calling tree (hence it prunes it) by ignoring all but the first publication of initial orchestration. In fact, because when the first publication is made there is no need to keep wasting resources on computing the first orchestration, it is terminated immediately.

This operator is a replacement for the where operator of Orc. In Orc, both sub-orchestrations are run in parallel; in HOrc though, the initial sub-orchestration is run until a value is published after which it is terminated and the second sub-orchestration is started. A small, useful example of the functionality of this operator is to create computations that may time-out. Assume that makeCall is a computation that may take an arbitrary amount of time.

either (oprint "Timed Out!") (oprint) `prune` (ortimer 2 >>= olet . Left) `mplus` (makeCall >>= olet . Right)

Starting from the right-hand side we see that a timer for 2 seconds and makeCall are started simultaneously -- the right-hand sides of each sub-orchestration serve simply to unify the values published by each expression into one type. After that, the prune operator will take care of picking only the first value published by either one of them and pass it to the either, which will check from whom the value came -- Left or Right -- and act accordingly -- if the timer finished first then "Timed Out!" is printed, otherwise, the value returned by makeCall is printed.

--++ Running Orchestrations

One think we failed to mention so far is how the whole process of running an orchestration are started. To that end, one must use one of the run functions provided with HOrc. The first one is pretty simple:

run :: Action a -> IO ()

It simply runs the action and discards the values published by the orchestration. A slightly more complicated but much more useful runner is runC -- short for run and collect -- which collects all the values published by an orchestration and returns them in a list.

runC :: Action a -> IO [a]

This list can then be used on your Haskell programs. The list will only be returned all at one -- there's no lazyness -- when the orchestration terminates; this limits the usefulness of the operator as it is not possible to process the values published by the orchestration as they are generated but only in one go. Sadly, this is a limitation we have not been able to overcome.

What Now?

We hope to have given you a pleasant taste of what HOrc is capable. If we managed to get your interest then you should probably just go to the Downloads section and get HOrc and the other examples we have implemented on top of it; or even better, start using it yourself and create your own orchestrations! With the power of functional programming provided by Haskell and the beauty of the concepts behind Orc you should be able to conquer the world!

-- MarcoDevesasCampos - 30 May 2009

r2 - 31 May 2009 - 14:03:16 - MarcoDevesasCampos
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback
Syndicate this site RSSATOM