Proceedings of DSL'99: The 2nd Conference on Domain-Specific Languages

Austin, Texas, USA, October 3–6, 1999

M O N A D I R O B O T I C S

John Peterson and Greg Hager

THE ADVANCED COMPUTING SYSTEMS ASSOCIATION

© 1999 by The USENIX Association All Rights Reserved For more information about the USENIX Association: Phone: 1 510 528 8649 FAX: 1 510 548 5738 Email: [email protected] WWW: http://www.usenix.org Rights to individual papers remain with the author or the author's employer. Permission is granted for noncommercial reproduction of the work for educational or research purposes. This copyright notice must be included in the reproduced paper. USENIX acknowledges all trademarks herein.

Monadic Rob otics

John Peterson

Yale University

p [email protected], http://www.cs.yale.edu/homes/p eterson-john.html

Greg Hager

The Johns Hopkins University

[email protected], http://www.cs.jhu.edu.dom/~hager

Abstract 1 Intro duction

A successful DSL combines the vo cabulary values

and primitive op erations of an underlying domain

with abstractions that capture useful patterns in the

vo cabulary. Ideally, these abstractions organize the

vo cabulary into structures that supp ort clarity and

mo dularity in the domain of interest. In rob otic

control, this basic vo cabulary is quite simple: it

We have develop ed a domain sp eci c language for consists of feedback systems connecting the rob ot

the construction of rob ot controllers, Frob Func- sensors and e ectors. The more dicult task is to

tional ROBotics. The semantic basis for Frob is build complex b ehaviors by sequencing among var-

Functional Reactive Programming, or simply FRP, ious control disciplines, guided byoverall plans and

a purely functional mo del of continuous time, inter- ob jectives. Controllers must be robust and e ec-

active systems. FRP is built around two basic ab- tive, capable of complex interactions with an uncer-

stractions: b ehaviors, values de ned continuously in tain environment. While basic feedback systems are

time, and events, discrete o ccurances in time. On well understo o d, constructing controllers remains a

this foundation, we have constructed abstractions serious software engineering challenge. Many di er-

sp eci c to the domain of rob otics. Frob adds yet ent high-level architectures have b een prop osed but

another abstraction: the task, a basic unit of work no one metho dology addresses all problems, mak-

de ned by a continuous b ehavior and a terminating ing this is an ideal area for the application of DSL

event. technology.

This pap er examines two interrelated asp ects of Frob is an emb edded DSL for rob otic control sys-

Frob. First, we study the design of systems based tems. Frob is built on top of Functional Reactive

on FRP and how abstractions de ned using FRP Programming FRP, which is in turn built on top

can capture essential domain-sp eci c concepts for of Haskell, a lazy, purely

systems involving interaction over time. Second, we language[14]. Frob hides the details of low-level

demonstrate an application of monads, used here to rob ot op erations and promotes a style of program-

implementFrob tasks. By placing our task abstrac- ming largely indep endent of the underlying hard-

tion in a monadic framework, we are able to organize ware. It also promotes a declarative style of sp eci -

task semantics in a mo dular way, allowing new ca- cation: one that is not cluttered bylow level imple-

pabilities to b e added without p ervasivechanges to mentation details.

the system.

An advantage of Frob as well as many DSLs is that

We present several rob ot control algorithms sp eci- it is architecture neutral. That is, instead of de n-

ed using Frob. These programs are clear, succinct, ing a sp eci c system architecture organization or

and mo dular, demonstrating the power of our ap- basic design pattern it enables arbitrary architec-

proach. tures to de ned in a high level, reusable manner. As

an emb edded DSL, Frob includes the capabilities of ming systems has b een an area of active research

a fully-featured functional programming language, in rob otics see [2] for a recent collection of arti-

Haskell. cles in this area. Many of these languages are re-

alized by de ning data structures and certain sp e-

This pap er addresses b oth the Frob language itself, cialized library routines to existing languages no-

its capabilities, usage, and e ectiveness, and the im- tably C [6, 5, 10,16], Lisp [1], Pascal [11], and Basic

plementation of Frob. In particular, we examine the [15]. In particular, most of these \languages" in-

use of a monad to implement one of the essential se- clude sp ecial functions or commands that op erate

mantic comp onents of Frob. We demonstrate how in the time domain. For example, VAL includes a

\o the shelf " monadic constructs may b e incorp o- command to move the rob ot to a given spatial lo ca-

rated into a domain sp eci c language to express its tion. This command op erates asynchronously and

semantic foundation clearly and, more imp ortantly, has, thereby, the side-e ect of \stitching together"

in a mo dular manner. We address monads from multiple motions if they are supplied in rapid order.

a practical vantage rather than a theoretical one, Likewise, other emb edded languages such as the

emphasizing their usage and b ene ts within our do- \Behavior Language" of Bro oks [1] and the Reac-

main. tive Control Framework of Khosla [16] provide rich

programming environments for the co ordination of

This pap er contains many examples written in time-domain pro cesses. AML [17] is an exceptional

Haskell. Readers must have at least a passing fa- case | it is a language designed from scratch. As

miliarity with the syntax, primitives, and typ es of such, it supplies similar capabilities to VAL, but

Haskell. Those unfamiliar with Haskell will nd with the addition of enhanced error handling ca-

www.haskell.org has much helpful information. pabilities for rob ot program execution.

Although we make extended use of Functional Re-

active Programming, we attempt to explain FRP Despite the proliferation of rob ot programming sys-

constructs as they are used. No prior understand- tems, relatively little work has b een done on deriv-

ing of monads is required. ing a principled basis for them. For example, none of

the languages cited ab ove has a formal or in many

The remainder of this pap er is organized as follows. cases even informal! semantics. Counter to this

Section 2 discusses the domain of rob ot control and trend, Lyons [9] provides a comp ositional paradigm

essentials of FRP and monads. Section 3 demon- for expressing rob ot plans collections of atomic ac-

strates the construction of the task monad in an in- tions that are sequenced and co ordinated using a

cremental manner, adding features one by one and rich set of primitives. The notion of continuous b e-

examining the impact on the system as the de ni- havior and discrete event-based transitions is also

tion of a task changes. In Section 4, a number of intro duced. However, no transparent implementa-

non-trivial examples of Frob programming are pre- tion of the language or even realistic examples is

sented. Section 5 concludes. presented.

In contrast, Frob has a transparent, extensible, and

semantically clear basis, and is a practical, useful

to ol for implementing rob ot programming systems.

2 Background

While Frob is also a library emb edded in an exist-

ing language, the abstractions de ning Frob are at

a higher level than those used to emb ed controllers

2.1 The Problem Domain

in languages suchasC. While a controller emb ed-

ded in C or C++ could, in theory, handle the same

sort of abstractions wehave used in Frob, their im-

Programming rob ots op erating in the real world

plementation would b e much more dicult and the

provides a unique example of a programming system

reliability of the system would su er in the absence

op erating in conjunction with the physical world.

of a go o d p olymorphic typ e system.

As such, any system must co ordinate multiple on-

going control pro cesses, detect sp ecial events gov-

erning task execution, and supply data structures

and language primitives appropriate to the domain.

Unsurprisingly, the development of rob ot program-

2.2 Ab out Haskell is an anonymous function. For exam-

ple, you can pass a function as a pa-

rameter: f \y -> y + 2. There's no

di erence between f x y = x + y and

Since Frob inherits the syntax, typ e system, and

f = \x y -> x + y.

libraries of Haskell users of Frob must, of necessity,

learn ab out Haskell. This section contains a brief

 Typ e signatures: the p olymorphic typ es in

overview of the Haskell features used in Frob; those

Haskell are quite descriptive. Typ es are in-

familiar with Haskell may wish to skip to the next

ferred, allowing typ e signatures to b e omitted,

section.

but for claritywe include signatures in all ex-

amples. Typ e signatures supply valuable do cu-

Basic Haskell features include the following:

mentation and maketyp e errors easier to diag-

nose. The syntax of a signature declaration is

x :: Int, where this de nes the typ e of x to b e

 Variables: these start with lower case letters,

Int. Typ e signatures are generally placed im-

such as x or robotPosition. Variables may

mediately preceding the asso ciated de nition.

include _ and ' characters, so x' is a also a vari-

able name. Op erators are comp osed of punctu-

 Function typ es: the typ e of a function from

ation, suchas-=> or .|..

typ e t1 to typ e t2 is written t1 -> t2.

A function with more than one argument

 Function application: Haskell do es not use

will have more than one arrow in its typ e.

fx,y style notation for function calling. In-

Watch for parenthesis, though: the typ e

stead, the parenthesis and commas are omit-

f :: Int -> Int -> Int de nes a function

ted, as in f x y. Parentheses may b e used for

with one argument, a function from Int to Int,

grouping, as in f g x h y, which would

rather than two Int arguments; that would b e

b e written fgx,hy in other languages.

f :: Int -> Int -> Int.

 In x op erators: examples include x + y or

 : you don't need to pass all of the

e -=> f. An in x op eration is converted into

arguments to a function at once. A call to

an ordinary variable using parentheses, as in

f :: Int -> Int -> Int without the second

-=> or +. Ordinary functions, such as

argument, as in f 3, results in a function that

f, can be used in the in x style when sur-

takes the remaining argument.

rounded by backquotes, as in x `f` y, which

is the same as f x y. Application takes prece-

 Polymorphic typ es: lower case identi ers in

dence over in x op erators, so f x+y z parses

typ e expressions are typ e variables. These

as f x+y z.

scop e over a single typ e signature and denote

typ e equality. Manytyp es are parameterized;

 Layout: indentation separates de nitions: each

the parameters are passed using the same syn-

de nition in a list must start in the same col-

tax that expressions use. For example, the sig-

umn and the list is terminated byanything in

nature

a preceeding columm. For example, in

==> :: Event a -> a -> b -> Event b

let x = 1

y = 2

de nes an op erator, ==>, that takes an Event

in x+2

parameterized over some typ e a and a func-

tion from typ e a to another typ e b, yielding

the indentation of the y must exactly match

an Event parameterized over typ e b. Polymor-

that of x.

phic typ es are an essential part of Frob; many

built-in Frob op erators are may b e almost com-

 De nitions: the = in Haskell creates a de ni-

pletely describ ed by their typ e signature.

tion. You can de ne a constant, as in x = 3,

or a function, as in f x y = x + y. As with

 Contexts: In Haskell, overloading is manifested

function application, no parenthesis are needed

in typ e signatures that contain a context: a set

of constraints on typ e variables, pre xing an

around the function parameters.

ordinary signature. For example, the typ e

 Lambda abstractions: functions need not

be named. The expression \x y -> x + y > :: Ord a => a -> a -> Bool

indicates that two arguments to > are of the rerror :: Robot -> Behavior Float

rerror r =

same typ e and that this typ e must b e in class

limit

Ord. A Haskell typ e class is similar to a Java

velocity r * sin thetamax

interface.

setpoint - leftSonar r

where limit m v = -m `max` v `min` m

 Tuples and Lists: A tuple is a simple way

of grouping two or more values. Tuples use

parentheses and commas: x,y is an expres-

This example shows a function mapping rob ot sen-

sion that combines x and y into a single tu-

sors as selected by the velocity and leftSonar

ple. The elements of a tuple mayhave di erent

functions onto a time-varying oat, part of a larger

typ es. Lists are written using square brackets:

control system. The details of this example are

[1,2,3] is a list of three integers. The : op er-

unimp ortant; the p oint is that writing functions

ator adds a new element to the front of a list;

over b ehaviors is little di erent from writing func-

[1,2,3] = 1:2:3:[]. Many list functions are

tions for static value.

pre-de ned in Haskell.

Behaviors hide the underlying details of clo cking

 The Mayb e typ e: the typ e Maybe a denotes ei-

and sampling, presenting an illusion of continuous

ther a value of typ e a, Just x, or Nothing.

time. Behaviors also supp ort op erators not found in

Maybe is often used where C programmers

the static world: b oth integral and derivative,

would use a p ointer that maybevoid.

for example, exploit time ow. As a further exam-

ple of the expressive power of b ehaviors, consider

 The void typ e: the typ e , pronounced "void",

the following:

is used in situations where no value is needed.

The only value of this typ e is .

atMin :: Ord a =>

Behavior a -> Behavior b -> Behavior b

2.3 Functional Reactive Programming

This returns the value of the second b ehavior at the

time the rst b ehavior is at its minimum.

In developing Frob we have relied on our exp eri-

ence working with Fran, a DSL emb edded in Haskell

The other essential abstraction supplied with FRP

for functional reactive animation [4, 3]. Functional

is the event. The typ e Event t denotes a pro cess

Reactive Programming can be thought of as Fran

that generates discreet values messages of typ e

without animation: the basic events, b ehaviors, and

t at sp eci c instances. Some comp onents of the

reactivity without op erations sp eci c to graphics.

system are b est represented by events rather than

b ehaviors. For example, the bump ers are of typ e

At the core of FRP is the notion of dynamical ly

Event BumperEvent; o ccurances happ en when one

evolving values. Avariable in FRP may denote an

of the rob ot bump er switches is activated. The con-

ongoing pro cess in time rather than a static value.

sole keyb oard has typ e Event Char, where eachkey-

There are two sorts of evolving values: continuous

press generates an event o ccurance.

b ehaviors and discrete events. A b ehavior is de ned

for all time values while events haveavalue only at

Events may b e synthesized from b o olean b ehaviors,

using the predicate function:

some discrete set of times.

For a typ e t, the typ e Behavior t is an evolv-

predicate :: Behavior Bool -> Event 

ing quantity of typ e t. Behaviors are used to

mo del continuous values: avalue of typ e Behavior

SonarReading represents the values taken from the

This can be thought of monitoring a b o olean be-

sonars; Behavior Point2 represents the p osition of

havior and sending a message when it b ecomes true.

the rob ot. Expressions in the b ehavioral domain are

This de nition uses predicate to generate an event

not signi cantly di erent from static expressions.

when an underlying condition rst holds:

Through overloading, most static op erators op erate

also in the dynamic world: users see little di erence

stopit :: Robot -> Event  between programming with static values and with

stopit r = predicate b ehaviors. For example, the following declaration is

time > timeMax || frontSonarB r < 20 typical of Frob:

section we will attempt to demystify monads some- This event o ccurs when either the current time

what. passes some maximum value or when an ob ject ap-

p ears less than 20 cm away on the front sonar of the

First, why don't C programmers or Java program- rob ot.

mers use monads? The answer is really quite sim-

ple: monads don't actually do anything new. Mon- Within FRP, a rob ot controller is simply a function

ads are used for state, exceptions, backtracking, and from the rob ot sensors, represented using b ehaviors

many other things that programmers have long done and events bundled into the Robot typ e, onto its ef-

without monads. What the monad do es is allow fectors, b ehaviors and events that drive the wheels

many well-understo o d constructs to be explained and any other systems controlled by the rob ot. The

conveniently in purely functional terms. Outside a ow of time is hidden with the FRP abstractions;

purely functional language, it's usually easier but the user sees a purely functional mapping from in-

mayb e not b etter to do what you want directly puts to outputs.

without involving monads.

Is this all we need? Perhaps; complex rob ot con-

In a pure language, though, monads are an ideal trollers can b e constructed using only the basic FRP

way to capture the essential semantics of a domain primitives. However, such controllers haveanum-

without compromising purity or mo dularity. Mon- b er of problems:

ads hide the \gears and wheels" of the domain from

the user while also presenting a simple, intuitive in-

 While FRP is well suited for the low-level con-

terface to the user. The user as opp osed to the DSL

trol systems present in this domain, it lacks

designer sees only sequencing and the return op er-

higher level constructs needed to plainly ex-

ator, together with `magic' functions that reach in-

press rob ot b ehaviors at a high level.

side to these gears and wheels in some way. Monadic

programming is made more readable in Haskell us-

 Controllers may b e complicated by \plumbing"

ing the do notation. Users of the DSL don't really

co de needed to propagate values in a functional

have to know anything ab out monads at all; they

manner.

simply \wire up" their program using do and the

rest of the monadic internals are unseen.

 Hard to understand FRP constructs are some-

times required. While users easily comprehend

The most imp ortant feature of the monadic ap-

basic event and b ehavioral op erators, FRP is

proach is mo dularity: new features may be added

also littered with arcane but essential op era-

without breaking existing co de. Under the hood,

tors suchassnapshot, switcher,or withElem

though, interactions b etween di erent features in a

that are unfamiliar to users and are not a nat-

monad are out in the op en. This is the advantage of

ural part to the underlying domain.

a purely functional style: the interplay among these

various features is very explicit.

Our goal is to create b etter abstractions: ones that

Another advantage of monads is that there are

emb o dy patterns that are familiar to domain engi-

many \o the shelf " monadic constructions avail-

neers and that have well-de ned semantic prop er-

able. There is no need to re-invent a basic semantic

ties.

building blo ck such as exception handling; this is

already well understo o d. A DSL designer may com-

bine many such building blo cks into a very domain-

2.4 Monads as a Mo dular Abstraction

sp eci c monad. There are also a number of alge-

Tool

braic prop erties that make monadic programs easier

to understand and reason ab out.

Monads have b een surrounded by a great deal of

Going back to a very concrete level, a monad in

hyp e in the functional programming community.

Haskell de ned with an instance declaration that

This has led those outside of this communitytoask

asso ciates a typ e with the two monadic op erations

questions such as \What the heck are monads any-

in the class Monad:.

way?", \If monads are so useful why don't they have

them in Java?", and \Do I have to understand cat-

egory theory to write Haskell programs?". In this class Monad m where

vo cabulary. >>= :: m a -> a -> m b -> m b

return :: a -> m a

Perhaps the b est intro duction to the practical use

of monads is Wadler's \The Essence of Functional

The op erators are simple enough: >>= or bind

Programming"[18].

is sequential comp osition while return de nes an

\empty" computation. A sp ecial syntax, do no-

tation, makes calls to >>= more readable. In ad-

dition to this instance declaration, other functions

may reach inside the monad, ho oking into its inter-

3 Implementing Frob

nals. For example, consider the state monad. Here,

the bind and return de ne the propagation of the

state from computation to computation. To actu-

The basic implementation of Frob is discussed in [13]

ally reach inside to the state, additional functions

and [12]. Here, we examine only tasks and their use

must be written to get at this internal state. The

of monads.

following example shows the declarations needed to

de ne a monad over a container typ e T that, inter-

nally, maintains a state of typ e S:

3.1 The Basic Task Monad

data S = ...

Rather than present the full de nition of Frob tasks -- A computation in T that returns type a

-- is a function that takes a state and returns

up front, we will instead develop the task abstrac-

-- an updated state and an a.

tion incrementally, adding features one by one and

showing how each incremental extension in the ex-

data T a = T \S -> S, a

pressiveness of tasks a ects programs and the task

implementation. Our purp ose here is twofold: to

instance Monad T where

show the ability of functional reactive programming

T f1 >>= c2 =

to de ne the abstractions needed for our domain

T \state ->

and, more imp ortantly, to show how the use of

let state', r = f1 state

a monad to organize the task structure promotes

T f2 = c2 r in

mo dularity.

f2 state'

return k = T \state -> state, k

The essential idea b ehind a task is quite simple: the

typ e Task a b de nes a b ehavior actually,any re- getState :: T S

activevalue, a,over some duration and then exits getState = T \state -> state, state

with a value of typ e b. In terms of FRP, a task is

represented as setState :: S -> T 

setState state = T \_ -> state, 

behavior `untilB` event ==> nextTask

runT :: S -> T a -> S, a

runT state T f = f state

where untilB switches the b ehavior up on o ccurance

The Monad instance explicates the passing of the

of the terminating event. The ==> op erator passes

state from the rst computation to the second. The

the value generated by the event to the next task.

getState and setState reach inside this particular

Tasks are a natural abstraction in this domain: they

monad to access the normally hidden state. Finally,

couple a continuous control system a b ehavior

the runT function runs a computation in monad T,

with an event that moves the system to a new mo de

passing in an initial state and pro ducing the nal

of op eration. Tasks are not restricted to the top

state and returned value.

level of the system: any reactivevalue eventorbe-

havior may b e de ned as a task; many tasks may

We may add new capabilities to the state monad

b e active at one time.

exception handling, for example without chang-

ing any of the user level co de that uses the monad.

Initially, the task monad requires only one instru-

That is, wemay often enrich a monad's vo cabulary

ment from the monadic to olb ox: a continuation to

carry the computation to the next task. This is without altering \sentences" expressed in the old

implemented byatyp e, Task, and an instance dec- rate. The runController function executes a con-

laration for the standard Haskell Monad class:

troller, a function from sensors Robot to e ectors

WheelControlB. Since runAround is not a termi-

nating task there is no reason to pass a nal b ehav-

data Task a b = Task b -> a -> a

ior to runTask.

unTask Task t = t

-- standard continuation monad

Starting with this foundation the monad of contin-

instance Monad Task where

uations, mkTask to build atomic tasks, and runTask

Task f >>= g =

to pull a b ehavior out of the task monad, we will

Task \c -> f \r -> unTask g r c

now add some new features.

return k =

Task \c -> c k

In the previous example, the rob ot description had

to be passed explicitly into each part of the con-

This de nes a structure for combining computations

troller. We can pass this description implicitly

the glue; we still need to de ne the computations

rather than explicitly by building it into the task

themselves. Here is a simple task creation function:

monad directly. That is, wewant to pass the rob ot

description to runTask and then have it app ear

mkTask :: Behavior a, Event b -> Task a b

wherever needed without adding extra r parame-

mkTask b,e =

ters to everything. In particular, the place we really

Task \c -> b `untilB` e ==> c

need it to app ear is mkTask, since the b ehavior and

event are generally functions of the current rob ot.

Now that we can create tasks and sequence tasks,

We de ne a new typ e to encapsulate task state:

how do we get out of the Task world? After all,

the rob ot controller is de ned in terms of b ehaviors,

not tasks. That is, we need to convert a task into

data TaskState =

a b ehavior. This brings up a small problem: what

TaskState {taskRobot :: Robot}

to do when the task completes? That is, what is

the initial value of the continuation argument? One

way out of this dilemma is to pass in an additional

For now, our state has only one element: the current

b ehavior to take control after the task exits:

rob ot. The typ e TaskState is de ned using Haskell

record syntax, which here de nes taskRobot as a

selector function to extract the rob ot from the task

runTask :: Task a b -> a -> a

state. As more comp onents are added to the task

runTask Task t finalB = t const finalB

state, the de nition of TaskState will change but

co de referring to state values will remain unaltered.

We now have everything needed to write a simple

We now add this state to the de nition of Task.

rob ot controller. Here are two simple tasks:

A task is given an initial state the rst parameter

and then passes a p otentially up dated state to the

continuation carrying the next task:

goAhead, turnRight, runAround ::

Robot -> Task WheelControlB 

goAhead r =

data Task a b =

mkTask pairB 10 0

Task TaskState ->

predicate frontSonar r < 20

TaskState -> b -> a -> a

turnRight r =

instance Monad Task where

mkTask pairB 0 0.5

Task f >>= g =

predicate frontSonar r > 30

Task \ts c ->

runAround r = do goAhead r

f ts \ts' r ->

turnRight r

unTask g r ts' c

runAround r -- loop forever

return k =

main =

Task \ts c -> c ts k

runController

\r -> runTask runAround r undefined

Again, this instance de nition is \o the shelf ": a

standard combination of continuations and state.

The wheel controls are de ned by a pair of num-

The runTask function now needs an initial state to

b ers, constructed using pairB, with the rst be-

pass into the rst task. The de nition of runTask

is now: ing the forward velo city and the second the turn

runTask :: TaskState -> Task a b -> a -> a

runTask ts Task t finalB = main =

t ts \_ _ -> finalB runController

\r -> runTask

TaskState {taskRobot = r}

In general, the call to runTask will need to ll in

runAround r

undefined

initial values for all comp onents of the task state.

We've put TaskState into the monad, but how

Note that the comp osite task, runAround, is not

can we get it back out again? That is, how can

aware of the propagation of the task state. We

tasks access information inside TaskState? These

could have retained the old mkTask without the

monadic op erators directly manipulate the current

TaskState argument for compatibility but have

TaskState:

chosen not to. This change could, though, have b een

made without invalidating any user co de.

getTaskState :: Task a TaskState

getTaskState = Task \ts c -> c ts ts

We have, so far, exploited well known monadic

setTaskState :: TaskState -> Task a 

structures for continuations and state. One more

setTaskSTate ts = Task \_ c -> c ts 

basic monadic construction is of use: exceptions.

With exceptions, tasks of typ e Task a b may suc-

ceed, returning a value of typ e b, or fail, raising an

We also make the state available to tasks de ned via

exception of typ e RoboErr. This is re ected in a

mkTask. The argumentto mkTask is now a function

new de nition of the Task typ e in which a task may

from the current task state onto the b ehavior and

return either its terminating event value, b, or an

event de ning the task:

error value, of typ e RoboErr.

mkTask :: TaskState ->

data Task a b =

Behavior a, Event b ->

Task RState ->

Task a b

RState -> Either RoboErr b -> a -> a

mkTask f =

Task \ts c ->

These primitives raise and catch exceptions:

let b,e = f ts in

b `untilB` e ==> c

taskCatch ::

Task a b ->

The de nitions in the previous example are now sim-

RoboErr -> Task a b ->

pli ed: the rob ot is propagated to the tasks implic-

Task a b

itly rather than explicitly:

taskError :: -- Raise an error

RoboErr -> Task a b

goAhead, turnRight, runAround ::

Task WheelControlB 

We omit the de nitions of these primitives and the

mo di ed Monad instance; these are standard con-

goAhead =

structions along the lines of those in [18]. How-

mkTask \ts ->

ever, we will examine the changes needed to mkTask.

let r = taskRobot ts in

pairB 10 0, That is, the Monad instance itself is essentially inde-

predicate frontSonar r < 20

p endent of the underlying domain, de ned in terms

of standard monad constructions and unsp eci c to

turnRight =

rob otics while the task creator, however, is very

mkTask \ts ->

domain-sp eci c and must be mo di ed to account

let r = taskRobot ts in

for the presence of exceptions.

pairB 0 0.5,

predicate frontSonar r > 30

Here is a new version of mkTask that adds an error

event to the basic de nition of a task. We use a

runAround =

slightly di erent name, mkTaskE, so that the old in-

do goAhead

terface, mkTask, remains valid. The new de nitions

turnRight

runAround -- loop forever are:

e ==> Right .|. err ==> Left mkTask ::

`snapRobot` tsRobot ts TaskState -> Behavior a, Event b ->

==> \res,rstate -> Task a b

c addRstate ts rstate res mkTask f =

mkTaskE \ts -> let b,e = f ts in

b, e, neverE

The terminating event of the b ehavior is augmented

with the state of the rob ot at the time of the event

mkTaskE ::

by the the snapshot function, a primitive de ned

Robot ->

FRP to capture the value of a b ehavior at the time

Behavior a, Event b, Event RoboErr ->

of an event o ccurance. Tasks will now nd this ini-

Task a b

tial orientation as part of the task state. This, a

mkTaskE f =

\turn right" task would b e as follows:

Task \ts c ->

let b,e,err = f ts in

b `untilB` e ==> Right .|.

turnRight =

err ==> Left

mkTask \ts ->

==> c

let goal =

initialOrient ts + 90 in ...

The only change here is that the terminating event

is either the normal exit event with the Right con-

Empty tasks those de ned as a return pass the

structor added or an error event, as tagged by Left.

state onto the next task unchanged.

The .|. op erator is FRP construct that merges

events, taking the rst one to o ccur. The ==> op er-

3.2 Task Transformations

ator mo di es the value of an event, so if err has typ e

Event RoboErr, then err ==> Right has typ e Event

Either a RoboErr. The constructors Left and

Having describ ed the elementary task op erations in

Right de ne the Either typ e.

detail, we now examine, brie y, other task op er-

ations. Given an existing task, what useful task

Next, consider a task such as \turn 90 degrees

transformations can b e implemented? Examples in-

right". Can we enco de this easily as a Frob task?

clude:

Not yet! The problem is that a task don't know

the orientation of the rob ot at the start of the task.

addError ::

We can build a control system to turn to a sp eci ed

Event RoboErr -> Task a b -> Task a b

heading, but howdowe know what the goal should

timeLimit ::

be? The answer lies in the task state. During task

Time -> Task a b -> Task a Maybe b

transitions the untilB in mkTaskE, we should also

withB ::

take note of where the rob ot is, whichway its p oint-

TaskState -> Behavior a ->

ing, and other useful information.

Task b c ->

Task b c,a

In this simpli ed example, we capture the current

withExit ::

rob ot lo cation when moving from task to task. The

Event a -> Task b c -> Task b a

monad remains unchanged except for a new eld in

withMyResult ::

the TaskState structure but the task builder must

a -> Task a b -> Task a b

b e mo di ed as follows:

withFilter ::

a -> a -> Task a b -> Task a b

withPicture ::

type RobotStatus = Point2

Behavior Picture -> Task a b -> Task a b

snapRobot ::

Event a -> Robot -> Event a,Radians

The op eration of many of these functions is obvious

snapRobot e r =

from the typ e signature: an illustration of the value

e `snapShot` orientationB r

of p olymorphic typ e signatures as do cumentation.

While the full implementations of these functions

mkTaskE f =

is beyond the scop e of this pap er, we will provide

Task \ts c ->

a basic outline of each of these functions and how

let b,e,err = f ts in

b `untilB` they are supp orted by the Task monad.

withMyResult f = Most of the interesting semantic extensions to the

Task \ts c -> let r = unTask t ts c system involve the basic de nition of an atomic task.

t = f r in By bringing values from the task state into this

e de nition, we can parameterize sequences of tasks

rather then atomic ones. For example, consider

addError: this function adds a new error eventto

Another place in which the atomic de nition of a

an existing task. Note that the error event sp ec-

task may b e further parameterized is the resulting

i ed in mkTaskE applies only to an atomic task.

b ehavior. That is, instead of

The task passed to addError, however, may con-

sist of many sequenced subtasks. The de nition of

addError lo oks something like this:

b `untilB` e ...

addError err tsk

we mo dify the resulting b ehavior using a lter in

do oldErr <- previous global error event

the task state:

let newErr = err .|. oldErr

place newErr into the task state

execute tsk

getfilter ts b `untilB` e ...

restore prior global error event

This is implemented in a manner similar to

Of course, mkTask must b e changed to o. The event

withError, using

err now b ecomes err .|. getGlobalErr ts: the

error event in the task state must b e included in the

withFilter ::

error condition in the untilB. This sort of \scop ed

a -> a -> Task a b -> Task a b

reactivity" is not easily expressed in basic FRP; us-

ing the task monad makes it much easier to imple-

ment this feature.

Finally,we discuss a more global change to the task

structure. Debugging controllers is dicult: it is

The timeLimit function ab orts a task if it do es not

hard to visualize the op eration of a control system

complete within a sp eci ed time. It is implemented

based on printing out numb ers on the screen as the

using addError to attachanevent that to the asso-

controller executes. Amuch b etter debugging tech-

ciated task which o ccurs at the sp eci ed time. This

nique is to display diagnostic information graphi-

requires b oth the exception and state capabilities of

cally in the rob ot simulator, painting various cues

the underlying monad.

onto the simulated world to graphically convey in-

formation. For this, we augment the b ehavior de-

The withB function de nes a b ehavior to run in par-

ned by a task to include an animation. That is,

allel with a task. When the task exits, the value of

using the task monad we provide an implicit chan-

the b ehavior is added to the task's result value. This

nel to convey diagnostic information along with the

is implemented by building a task that attaches a

b ehavior. This mo di cation requires changes to the

snapshotting function to the incoming continuation.

de nition of Task: a is replaced by a,Behavior

Picture, the typ e now pro duced by runTask. This

The withExit function ab orts a task up on an event.

change do es not a ect user-level co de; the extra pic-

If the task completes b efore the ab orting event, an

ture is implicit in every task. When a program is

error o ccurs. This is implemented directly in FRP

running on a real rob ot, the animation coming out

by untilB, as in this simpli ed de nition:

of runTask is ignored.

withExit e Task t =

This is the de nition of withPicture:

Task \ts c -> t ts err `untilB` e ==> c

where err = error "Premature task exit"

withPicture ::

Behavior Picture -> Task a b -> Task a b

This function is implemented directly at the contin-

withPicture p t =

uation level.

addFilter addPicture p t

The withMyResult function it allows a task to ob-

The addPicture function intro duces an additional serve its own result, which is often needed in the

picture to an augmented b ehavior. For example, di erential equations that de ne a controller.

of the overall task state. Lo cal error handlers and this function makes driveToGoal easier to under-

stand in simulation:

lters are removed from the state so that only t2

inherits these.

driveToWithPicture goal =

driveTo goal `withPicture`

paintAt goal withColor red circle

4 Examples

3.3 Parallel Tasks

Assessing a DSL is often dicult; di erent DSL's

are designed with di erent goals. Since our goal

So far, we have only mo di ed tasks or combined

is to build a language that is declarative and de-

them sequentially. Now, we wish to combine tasks

scriptive, we cho ose to assess it with examples of

by merging the results of multiple tasks running in

co de rather than p erformance gures. These exam-

parallel. The withTask function is the basic primi-

ples are chosen to demonstrate expressiveness rather

tive for combining tasks in parallel:

than computational sp eed.

withTask :: t2b -> Task t1b t1e ->

4.1 The BUG Algorithm

t1b ->

Event Either RoboErr t2e ->

Task t2b t2e ->

Task t2bt2e

First, we demonstrate the use of tasks to implement

awell known control strategy. BUG [7] is an algo-

rithm to navigate around obstacles to a sp eci ed

This initiates two tasks, each observing the b ehavior

goal. When an obstacle is encountered, the rob ot

de ned by the other. The termination of the rst

circles the obstacle, lo oking for the p oint closest

task, either through an exception or normal termi-

to the goal and the returns to this p oint to re-

sume travel. The following co de skeleton imple-

nation, may be observed by the other task as an

ments BUG in terms of two primitive b ehaviors:

event.

driving straight to a goal, and following a wall.

The driveTo task returns a b o olean: true when

The co de asso ciated with withTask is as follows:

the goal is reached, false when the rob ot is blo cked.

The followWall runs inde nitely, traveling in cir-

cles around an obstacle. If for some reason the wall

withTask t1f t2f =

disapp ears from the sonars, this task raises an ex-

Task \ts c ->

ception.

let t1e = makeNewEvent

t1b = unTask t1

cloneState ts

-- Basic tasks and events not shown

sendTo t1e

followWall ::

t2b = unTask t2 ts c

Task WheelControlB 

t1 = t1f t2b

driveTo ::

t2 = t2f t1b t1e in

Point2 -> Task WheelControlB Bool

t2b

atPlace ::

Robot -> Point2 -> Event 

Note the somewhat imp erative treatment of the

bug ::

terminating event of t1. The sendTo and

Point2 -> Task WheelControlB 

makeNewEvent functions exploit FRP internals, an

bug g =

exp edient but semanticly unusual way of dealing

taskCatch bug g -- restart on error

with events. The sendTo function b ecomes an un-

do finished <- driveTo g

de ned b ehavior after sending the termination mes-

if finished

sage; reference to the value of t1 after this event will

then return 

result in a runtime error.

else goAround g

More imp ortantly, notice the treatment of the task

goAround ::

Task WheelControlB  state. The task t1 needs to receive a \fresh copy"

goAround g = 5 Conclusions

do closestPoint <- circleOnceP g -- circle

circleTo closestPoint -- then back

bug g -- restart

We have demonstrated b oth a successful DSL for

rob otic control and shown how a set of to ols de-

circleOnceP ::

velop ed in the functional programming community

Point2 -> Task WheelControlB Point2

enable the construction of complex DSLs with rela-

circleOnceP g =

tively little e ort.

do _,p <- withB closestP circleOnce g

return p

Assessing a DSL or any programming language

where closestP ts =

is dicult at b est. We feel the success of Frob is

let r = taskRobot ts in

distance place r g `atMin` place r demonstrated in a number of ways:

circleOnce =

 Users from outside of the FP community nd

do ts <- getTaskStatus

let initp = initialPlace ts

that Frob is easy to use and well-suited to the

r = taskRobot ts

task of rob ot control. The abstractions sup-

-- get away from initial place

plied byFrob may b e understo o d at an intuitive

timeLimit followWall 5

level. While there is a de nite learning curve,

followWall `withExit` atPlace initp

esp ecially with resp ect to the Haskell typ e sys-

tem, users so on b ecome accustomed to p oly-

morphic typing and nd Haskell typ es much

This de nition is quite close to the informal de ni-

more descriptive than those of ob ject oriented

tion of BUG. Some necessary details have b een lled

systems.

in: what to do if the wall disapp ears from the sen-

sors this is caught at the top level and restarts the

 We have enco ded a number of well-known al-

system, how to circle travel for 5 seconds to get

gorithms and architectural styles in Frob and

away from the start p oint and then continue until

found the results to be elegant, concise, and

the start p oint is re-attained.

mo dular.

 As an emb edded DSL, Frob inter-op erates eas-

ily with other DSLs. Preliminary work on com-

4.2 A Pro cess Architecture

bining Frob with FVision a very di erent DSL

for vision pro cessing suggests that at least

some DSLs may be combined to great advan-

As another example, consider Lyons' approach of

tage.

capturing rob otic action plans as networks of con-

 Monads supp ort relatively painless evolution of

current pro cesses [8, 9]. Frob tasks can easily mimic

DSL semantics. DSLs are, rather naturally,

Lyons' pro cesses. His conditional comp osition op er-

somewhat of a moving target: as domain en-

ation is identical to >>= in the task monad with ex-

gineers and DSL implementors work together,

ceptions. Of more interest is parallel comp osition:

improvements in semantic expressiveness are

his P | Q executes pro cesses P and Q in parallel,

continually b eing develop ed. The monadic

with p orts connecting P and Q. This can b e directly

framework has allowed this semantic evolution

implemented with withTask. His disabling comp o-

to pro ceed without requiring constant rewriting

sition, P  Q is also contained in withTask,however

of existing co de.

withError is also needed to correctly disable the re-

sulting task when one task ab orts.

Some issues are still unresolved or unaddressed: we

The semantics of Frob p ermits the

have not yet p orted the system to new typ es of

following op erations synchronous concurrent com-

rob ots or implemented systems in which system p er-

p osition and asynchronous concurrent comp osition

formance is critical. We also haveyet to exp eriment

to b e implemented directly:

with multi-rob ot systems. Frob has not b een used

in anyway that taxed system p erformance; the data

P <> Q = do { v<-P; Q; P<>Q }

rates from our sensors are so low that the controller

P >< Q = do { v<-P; Q | P>

has relatively little work to do.

One particularly large part of this domain that has [2] E. Coste-Mani ere and B. Espiau, editors. In-

yet to be addresses is real time. For example, we ternational Journal of Robotic Research, Spe-

cannot express the priorities of various activities, al- cial Issue on IntegratedArchitectures for Robot

lowing Frob to direct resources toward more critical Control and Programming,volume 17:4, 1998.

systems when needed. No guarantees are made with

[3] Conal Elliott. Comp osing reactive animations.

resp ect to resp onsiveness or throughput. We exp ect

Dr. Dobb's Journal, July 1998. Extended

that Frob is capable of addressing these issues but

version with animations at http://-

much more complex analysis and co de generation

research.microsoft.com/~conal/fran/-

may be required. However, for high level system

ftutorial.htm,tutorialArticle.zipg.

control as exempli ed by the BUG algorithm these

issues are of less imp ortance.

[4] Conal Elliott and Paul Hudak. Functional reac-

tive animation. In International Conferenceon

We have used Frob to teach an undergraduate

Functional Programming, pages 163{173, June

rob otics course. Frob was quite successful in al-

1997.

lowing assignments that traditionally required many

pages of C++ co de to b e programmed in only a page

[5] V. Hayward and J. Lloyd. RCCL User's Guide.

or two. While there was an admittedly steep learn-

McGill University, Montre al, Qu eb ec, Canada,

ing curve, students eventually b ecame quite pro duc-

1984.

tive. The addition of graphic feedback in the simu-

[6] K. Konolige. Colb ert: A language for reactive

lator was esp ecially useful to them.

control in sapphira. In G. Brewka, C. Hab el,

and B. Neb el, editors, Advances in Arti cial

Turning to the issue of DSL construction, this re-

Intel ligence, volume 1303 of Lecture Notes in

search shows that monads are an imp ortant to ol

Computer Science. Springer, 1997.

for attaining program mo dularity. The de nition of

task monad evolved signi cantly over the term but

[7] V.J. Lumelsky and A.A. Stepanov. Dynamic

the interfaces remained the same, allowing all stu-

path planning for a mobile automaton with lim-

dent co de to run unchanged as Frob evolved. Mon-

ited information on the environment. IEEE

ads e ectively hide p otentially complex machinery

Trans. on Automatic Control, 3111:1058{63,

and provide a framework whereby new functionality

1986.

can b e added to a system with minimal impact on

existing co de.

[8] D. Lyons and M. Arbib. A formal mo del of

computation for sensor-based rob otics. IEEE

All Frob software, pap ers, and manuals are available

Trans. on Robotics and Automation, 63:280{

at http:haskell.org/frob. Nomadics has agreed

293, 1989.

to license their simulator to Frob users at no cost,

[9] Damian M. Lyons. Representing and analyz-

allowing our software to be used by those without

ing action plans as networks of concurrent pro-

real rob ots to control.

cesses. IEEE Transactions on Robotics and Au-

tomation, 97:241{256, June 1993.

[10] J.L. Mundy. The image understanding environ-

6 Acknowledgements

ment program. IEEE EXPERT, 106:64{73,

Decemb er 1995.

[11] Pattis, R et al. Karel the Robot. John Wiley & This researchwas supp orted by NSF Exp erimental

Sons, 1995. Software Systems grant CCR-9706747.

[12] J. Peterson, G. Hager, and P. Hudak. A lan-

guage for declarative rob otic programming. In

Proceedings of IEEE Conf. on Robotics and Au-

References

tomation,May 1999.

[13] J. Peterson, P. Hudak, and C. Elliott. Lambda [1] Ro dney Bro oks. A robust layered control sys-

in motion: Controlling rob ots with haskell. In tem for a mobile rob ot. IEEE Trans. on

Proceedings of PADL 99: Practical Aspects of Robotics and Automation, 21:24{30, March

Declarative Languages, pages 91{105, Jan 1999. 1986.

[14] John Peterson and Kevin Hammond. Haskell

1.4: A non-strict, purely functional language.

Technical Rep ort YALEU/DCS/RR-1106, De-

partment of Computer Science, Yale Univer-

sity,May 1997.

[15] B. Shimono. VAL:AVersatile Robot Program-

ming and Control Language. IEEE Press, 1986.

[16] D.B. Stewart and P.K. Khosla. The chimera

metho dology: Designing dynamically recon-

gurable and reusable real-time software us-

ing p ort-based ob jects. International Journal

of Software Engineering and Know ledge Engi-

neering, 62:249{277, 1996.

[17] R. H. Taylor, P.D. Summers, and J. M. Meyer.

AML: A manufacturing language. Int. J. of

Robot Res., 13:3{18, 1982.

[18] P.Wadler. The essence of functional program-

ming. In Proceedings of ACM Symposium on

Principles of Programming Languages. ACM

SIGPLAN, January 1992.