Proceedings of DSL'99: The 2nd Conference on Domain-Specific Languages
Austin, Texas, USA, October 3–6, 1999
M O N A D I C 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 functional programming
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
Currying: 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 lazy evaluation 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.