cs0010009/paper.tex
1: %%\documentclass[11pt]{article}
2: \documentclass{llncs}
3: 
4: %%\usepackage{paper}
5: \usepackage{times}
6: \usepackage{scriptcode}
7: 
8: % for drafting purposes
9: %\usepackage{remarks}
10: %\usepackage[notref,notcite]{showkeys}
11: 
12: \title{An Approach to the Implementation of Overlapping Rules in Standard ML}
13: \author{Riccardo Pucella}
14: \institute{Department of Computer Science\\Cornell University\\riccardo@cs.cornell.edu}
15: %%\author{Riccardo Pucella\\[.1in]
16: %%Department of Computer Science\\
17: %%Cornell University}
18: %%\date{\textbf{Draft of \today}}
19: 
20: 
21: \newcommand{\COMMENTOUT}[1]{}
22: \newcommand\kw[1]{\textbf{#1}}
23: \newcommand\expr[1]{\textit{#1}}
24: 
25: 
26: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27: 
28: \begin{document}
29: \maketitle
30: 
31: \begin{abstract}
32: We describe an approach to programming rule-based systems in Standard
33: ML, with a focus on so-called overlapping rules, that is rules that
34: can still be active when other rules are fired. Such rules are useful
35: when implementing rule-based reactive systems, and to that effect we
36: show a simple implementation of Loyall's Active Behavior Trees, used
37: to control goal-directed agents in the Oz virtual environment. We
38: discuss an implementation of our framework using a reactive library
39: geared towards implementing those kind of systems.
40: \end{abstract}
41: 
42: 
43: 
44: \section{Introduction}
45: 
46: Rule-based systems\footnote{In this paper, we focus exclusively on
47: production rule systems. Related systems, such as those based on a
48: notion of term rewrite rule, have not been considered at this point.}
49: have had a long history in AI and powerful 
50: implementations have been developed.  The
51: most problematic aspect of this work 
52: has always been that of integrating the rule-based approach to a
53: general-purpose language for application support. As an example along
54: those lines, in \cite{Crawford96}, Crawford \emph{et al} describe R++,
55: a rule-based extension to C++ completely integrated with the
56: object-oriented features of the language, implemented as a rewrite of
57: R++ into C++ code.  
58: 
59: \COMMENTOUT{
60: Our aim in this paper is somewhat similar, but along different
61: lines. We attempt to integrate rule-based programming with Standard ML 
62: (SML) \cite{Milner97}, in such a way as to be able to use both
63: rule-based code and ordinary SML code. Our integration should be as
64: much a library as possible, that is it should use facilities of the
65: }
66: 
67: One common use of rules, and indeed the primary motivation for this
68: work, is to help write rule-based reactive systems, reactive in the sense of having the
69: system react to changes in the environment. A change in the
70: environment should enable a certain number of appropriate rules that
71: can be fired to react to the environment change, possibly effecting
72: new changes to the environment that will enable other rules. 
73: 
74: \COMMENTOUT{With this 
75: point of view in mind, it is not surprising that the approach we
76: investigate relies on various reactive programming approaches. More
77: precisely, we investigate the use of a general reactive library
78: introduced in \cite{Pucella98} and based on the Reactive Approach of
79: Boussinot \cite{Boussinot91,Boussinot96b}. }
80: 
81: One aspect of rule-based reactive systems we focus on is that of
82: long-acting overlapping rules. In 
83: most rule-based systems, a single rule is active at any given time:
84: when the system determines that a rule is enable, the rule is fired,
85: the environment is updated, and a new rule can be selected. This is
86: not so great is some of the rules are computationally intensive: when
87: such a rule is fired, it will take time to execute and if the system
88: relies on other rules to ensure say responsiveness of the interface,
89: the system will not respond to the user until the rule has finished
90: executing. In general one, may want rules that span multiple other
91: rules firing. For example, one may pre-fire a rule when certain
92: conditions are met, perhaps pre-computing some values, and get ready
93: for the ``real'' firing once the ``real'' conditions are in place. Or
94: one may want a rule that when fired will perform a given action at
95: every subsequent rule firing until maybe a condition occurs that stops 
96: this behavior. It is of course possible to achieve these effects in
97: certain rule-based systems, by a process of chaining rules (at the end 
98: of one rule, setting up the firing conditions of the next rule), along
99: with a suitable notion of concurrently fired rules. 
100: 
101: In this paper, we describe an approach to the integration of
102: rule-based programming in Standard ML (SML) \cite{Milner97}, with a
103: strong focus on overlapping rules to achieve the effects described
104: above. The resulting framework is suitable for the design of
105: domain-specific abstractions for various rule systems. In contrast with 
106: other work, we do not worry about efficiency issues in this paper, but 
107: rather concentrate on expressiveness and applicability of the
108: framework. We assume throughout the paper a basic knowledge of SML, as 
109: described in various introductory material such as
110: \cite{Paulson96,Ullman98}. 
111: 
112: After reviewing the basic notions of rule-based programming in Section
113: \ref{s:review}, we discuss the framework in Sections
114: \ref{s:basic}--\ref{s:overlapping}. We give an application of the
115: flexibility of the framework in Section \ref{s:abts}, where we design
116: domain-specific abstractions for controlling a goal-directed
117: agent. Section \ref{s:impl} focuses on implementation details,
118: including a reference implementation in terms of an existing reactive
119: library for programming reactive systems in SML, introduced in
120: \cite{Pucella98}, and based on the reactive approach of Boussinot
121: \cite{Boussinot91,Boussinot96b}. 
122: 
123: 
124: \section{OPS-style production rules}
125: \label{s:review}
126: 
127: In this section, we establish the terminology and the model of rules
128: we are interested in, namely OPS-style production rules
129: \cite{Brownston85}. The \emph{production-system} model of  
130: computation is a paradigm on the same footing as the procedural
131: paradigm, the functional paradigm or the object-oriented paradigm: it
132: is a view of what a computation ought to be to best achieve a given
133: goal. 
134: 
135: A \emph{production-system program} is an unordered collection of basic 
136: units of computation called \emph{production rules} (henceforth simply 
137: called rules). Each rule has a condition part and an action part. An
138: \emph{inference engine} is used to execute the rules: it determines
139: which rules are \emph{enabled} by checking which conditions are true,
140: and select rules to execute or \emph{fire} from the enable rules. A
141: rule is fired by executing its action part, which typically will have
142: a side effect of performing input and output or computing a value and
143: updating some date in memory. Thus rule-based systems fundamentally
144: based on the notion of side effects.
145: 
146: Many mechanisms can be used to select which enabled rule to fire. In
147: the literature, the term \emph{conflict set} is often used to name
148: the set of rules which are enabled, reflecting the intuition that
149: somehow these rules can be conflicting, that is update the store in
150: different incompatible ways. To ensure such problems do not occur,
151: production systems will typically select a single rule to fire, by
152: methods involving various notions such as priority or probabilities,
153: along with notions such as the best matching of the conditions and so
154: on. 
155: 
156: In a rule-based system, control is \emph{data-driven}, that is the
157: data determines which part of the program will execute ---
158: furthermore, communication between different units is done solely
159: through the use of data. There is no concept of a subroutine call to
160: another rule, or anything of that sort. Rule-based systems allow a
161: cleaner separation of knowledge (in the form of rules) from the control
162: (encapsulated in the inference engine). This makes rule-based systems
163: well-suited to program expert systems for analysis problems, and for
164: programs for which the exact flow of control is not known. In this
165: paper, we will make the further refined point that with the
166: appropriate extensions, rule-based systems are well-suited for the
167: compositional development of reactive systems.
168: 
169: 
170: \section{A framework for rule-based programming}
171: \label{s:basic}
172: 
173: We begin by considering a general framework for the handling of simple OPS-style
174: rules in SML, where actions are executed atomically and terminate
175: before a new rule can be fired. We discuss the framework abstractly,
176: that is in terms its interface. 
177: 
178: The first notion of importance is that of a set of rules, as an
179: abstract type with a single basic operation that creates an empty set
180: of rules. The reason for keeping the set of rules abstract is to
181: allow for different implementations, some possibly aimed at optimizing 
182: the evaluation of the conditions.
183: \begin{code}
184:   \kw{type} rule_set
185:   \kw{val} newSet : unit -> rule_set
186: \end{code}
187: 
188: A rule is simply defined as a pair of a condition and an action, as in 
189: OPS.  Since we want the condition to be dynamically
190: evaluated, it is implemented as a function (the standard way to delay
191: evaluation in an eager language such as SML). A
192: condition evaluates to a positive integer (a word), which we call the
193: \emph{fitness} of the condition, indicating the degree to which the
194: condition is satisfied. There is no \emph{a priori} semantics or range 
195: associated with those, they are left to the discretion of the
196: programmer. The only restriction is that a fitness of zero is used to
197: indicate that a rule is not enabled. One can implement simple boolean conditions as values 0
198: and 1, if need be. \COMMENTOUT{A more involved implementation would push the
199: fitness into an abstract type with an associated ordering, which is
200: what we really require from words.} The only operation on rules is to
201: add them to rule sets. Note that because functions are first-class in
202: SML, rules become first-class as well. That is, they can be passed as
203: argument to functions, and returned from functions. 
204: \begin{code}
205:   \kw{type} rule = \{cond : unit -> word,
206:                action : unit -> unit\}
207:   \kw{val} addRule : rule * rule_set -> rule_set
208:   \kw{val} mkSet : rule list -> rule_set
209: \end{code}
210: 
211: The final ingredient of the system is the inference
212: engine, that selects which of the rules will be fired. At this point, the
213: issue of \emph{when} the rules should be fired must be
214: addressed. Many systems tie the firing of the rules, or at least the
215: evaluation of the conditions, to a change to variables that affect
216: the conditions. We choose a much more fundamental approach using an
217: explicit call that monitors the conditions, from which we can derive a
218: \emph{change-of-state} trigger. While inefficient, this approach has
219: the advantage of being general. The other issue this does
220: not address is that of conflict resolution, which rules to fire
221: of all those that are enabled. We provide a selection of conflict
222: resolution strategies. A function \expr{monitor} is used to select
223: rules to fire in a given set of rules. It takes as argument a conflict 
224: resolution flag determining the resolution strategy to use:
225: 
226: \begin{code}
227:   \kw{datatype} conflict_res = AllBest
228:                         | RandBest
229:                         | AllDownTo \kw{of} word 
230:                         | RandDownTo \kw{of} word
231:   \kw{val} monitor : conflict_res -> rule_set -> unit
232: \end{code}
233: \expr{AllBest} fires all the rules that qualify as the best rule to
234: apply, sorted according to fitness. \expr{RandBest} randomly picks one
235: of the rules that qualify as the best rule. \expr{AllDownTo} and
236: \expr{RandDownTo} perform similarly, but consider all the rules whose 
237: fitness is at least the given value. One can easily extend
238: the framework to allow for custom conflict resolution, which we do not
239: pursue in this paper for simplicity. 
240: 
241: Our notion of fitness is general. As we noted,
242: we can imagine a binary fitness (0-1) for boolean firing, but also a
243: fitness based on how close the conditions are to being completely
244: satisfied, or even so far as how many conditions are actually
245: satisfied (if we allow firing based on partially satisfied
246: conditions). An easy extension to the 
247: framework would be to pass information from the condition to the
248: action. We can mimic this easily by using a reference cell which can
249: also be hidden in a closure of the rule, as follows:
250: \begin{code}
251:   \kw{let} 
252:     \kw{val} r = ref 0
253:   \kw{in}
254:     \{cond = \textit{compute fitness, store something in r},
255:      action = \textit{some action using the value in r}\}
256:   \kw{end}
257: \end{code}
258: 
259: In striking difference with other systems, rules are not persistent in 
260: this framework: once a rule fires and executes, it is removed from the 
261: rule set. To make a rule persistent, we can use the function
262: \expr{persistent}. This allows us to dispense
263: with a function to remove rules 
264: from rule sets. Moreover, \expr{persistent} comes for
265: free given our extension for managing overlapping rules, as we will
266: see in the next section.
267: \begin{code}
268:   \kw{val} persistent : rule -> rule
269: \end{code}
270: 
271: As an example of how to use the framework, consider a simple
272: rule-based program to compute the greatest common divisor of two
273: integers, the classic Euclid's algorithm. The example is artificial
274: (it is easily implemented in SML without rules), but serves well to
275: illustrate the basics. A more complete example is presented in Section 
276: \ref{s:abts}. The program can be expressed
277: as follows in Dijkstra's language of guarded commands:
278: \[ \kw{do}~ X > Y \rightarrow X := X-Y ~|~ Y > X \rightarrow Y := Y - X ~\kw{od}\]
279: The corresponding code in our framework is more verbose, but
280: essentially similar:
281: \begin{code}
282:   \kw{fun} gcd (x,y) = \kw{let}
283:     \kw{val} rx = ref x
284:     \kw{val} ry = ref y
285:     \kw{fun} r (r1,r2) = persistent 
286:                       \{cond = \kw{fn} () => \kw{if} (!r1) > (!r2) 
287:                                          \kw{then} 1 \kw{else} 0,
288:                        action = \kw{fn} () => r1 := (!r1) - (!r2)\}
289:     \kw{val} rs = mkSet [r (rx,ry),r (ry,rx)]
290:     \kw{fun} loop () = \kw{if} ((!rx) = (!ry)) \kw{then} (!rx) 
291:                     \kw{else} (monitor AllBest rs; loop ())
292:   \kw{in}
293:     loop ()
294:   \kw{end}
295: \end{code}
296: 
297: The above example is interesting because it shows how the fact that
298: rules are first-class in the framework allow for parametrized rules:
299: the rule \expr{r} in the above code is parametrized over the reference 
300: cells containing the two arguments, parameterization which nicely
301: showcases the symmetry of the rules.
302: 
303: 
304: As a final remark, we note that the rules as we have presented them in
305: this section are simpler than they are in the actual framework. The rules
306: in the implemented framework contain an extra field generically called
307: \emph{data} whose type is a parameter to the structure implementing
308: the rules (in effect, the library is implemented as a functor). The
309: monitoring function takes as an extra argument a function to compute a 
310: fitness both from the result of the evaluation of the condition and
311: the data (which for example can contain notions such as rule priority
312: and so on). Since describing this explicitly would require us to go
313: into the details of both the module system and the type system of SML, we punt
314: on these issues in this paper. 
315: 
316: \COMMENTOUT{
317: One advantage of our approach is that we can fairly easily derive new
318: rule representations with hard-wired behavior. For example, assume we
319: want to implement prioritized rules. We can create a new structure
320: \begin{code}
321:   \kw{structure} PriorityRules :> \kw{sig}
322:     \kw{type} rule_set
323:     \kw{val} new_set
324:     \kw{type} rule = \{fitness : unit -> word,
325:                  action : unit -> unit,
326:                  priority : unit -> word\}
327:     \kw{val} addRule : rule_set * rule -> rule_set
328:     \kw{val} monitor : rule_set -> rule_set
329:   \kw{end} = \kw{struct}
330:     \kw{structure} PR = RulesFn (\kw{type} rules_data = unit -> word)
331:     \kw{type} rule_set = PR.rule_set
332:     \kw{val} new_set = PR.new_set
333:     \kw{type} rule = \{fitness : unit -> word,
334:                  action : unit -> unit,
335:                  priority : unit -> word\}
336:     \kw{fun} add_rule rs \{fitness,action,priority\} = PR.add_rule rs \{fitness=fitness,action=action,data=priority\}
337:     \kw{val} monitor = PR.monitor (\kw{fn} \{fitness,data\} => (fitness() * data ()),PR.RandBest)
338:   \kw{end}
339: \end{code}
340: }
341: 
342: \section{Managing overlapping rules}
343: \label{s:overlapping}
344: 
345: The main point of this work was to introduce \emph{overlapping rules}, 
346: that is rules that can span multiple invocations and be performed in 
347: parallel with other rules. From an interface point of view, the
348: framework only requires the addition of a single primitive, which we
349: call \expr{wait}, to add the desired functionality:
350: \begin{code}
351:   \kw{val} wait : (unit -> word) option\footnote{a primitive SML type defined as \cd{\kw{datatype} 'a option = NONE | SOME \kw{of} 'a}.} -> unit
352: \end{code}
353: 
354: The semantics of \expr{wait} is simple. Fundamentally,
355: \expr{wait} interrupts the action of the current rule, as if the rule
356: was finished executing, except that at the next time the monitoring
357: function is invoked to fire a rule, the rules that were interrupted are
358: allowed to resume while the new rule fires. Hence the term
359: \emph{overlapping}. In fact, the \expr{wait} primitive takes two
360: forms. The form \expr{wait (NONE)} behaves as an unconditional
361: interruption. Execution continues the next time the monitoring
362: function is invoked. The form \expr{wait (SOME (f))} with \expr{f} as
363: a condition (that is, a \expr{unit $\rightarrow$ word} function) 
364: also interrupts, but only resumes the rule the next time the
365: monitoring function is invoked with the condition \expr{f} being
366: satisfied. In effect, \expr{wait (SOME (f))} interrupts the rule and
367: conceptually replaces it with a new rule containing the remainder of
368: the interrupted rule, with a condition \expr{f}. 
369: 
370: As we mentioned in the previous section, rules are not persistent:
371: once they are fired and execute, they are removed from the rule
372: set.We can implement the \expr{persistent} function using \expr{wait}: 
373: \begin{code}
374:   \kw{fun} persistent \{cond,action\} = 
375:     \{cond = cond,
376:      action = \kw{let}
377:                 \kw{fun} loop () = (action (); 
378:                                wait (SOME (cond));
379:                                loop ())
380:               \kw{in}
381:                 loop
382:               \kw{end}\}
383: \end{code}
384: This nicely shows the power of first-class rules. 
385: 
386: 
387: 
388: 
389: \section{An application: goal-directed agent control}
390: \label{s:abts}
391: 
392: We describe in this section one of the motivating applications for the 
393: development of the framework in the first place, that of controlling
394: goal-directed agents. The architecture we have in mind is inspired by
395: Hap, a reactive, goal-driven architecture for controlling agents in
396: the Oz virtual environment \cite{Loyall91}. The main structure in Hap
397: is an \emph{active behavior tree} (ABT), which represents all the
398: goals and behaviors an agent is pursuing at any given point
399: \cite{Loyall97}. An agent chooses the next step to perform by selecting
400: one of the leaves of its ABT. Three types of actions can be performed
401: depending on the type of the node selected. 
402: \begin{enumerate}
403: \item \textbf{Primitive physical action}: an action sent to 
404: the action server, which can either succeed or fail depending on the
405: state of the world.
406: \item \textbf{Primitive mental action}: an action that simply performs
407: a computation (possibly with side effects) and which always succeeds.
408: \item \textbf{Subgoal}: an action corresponding to a new subgoal; an
409: appropriate behavior is selected that matches that subgoal, and the
410: ABT is expanded by adding the steps specified by the behavior to the
411: tree as children of the subgoal selected. 
412: \end{enumerate}
413: 
414: (For our purposes, we drop the distinction between mental and physical 
415: actions, since we can model mental actions as physical actions that
416: always succeed). Programming an agent reduces to programming behaviors
417: for various goals. Goals have no intrinsic meaning, they are simply
418: names on top of which the programmer can 
419: attach any semantics she desires. Behaviors are defined by specifying
420: to which goal they apply, a pre-condition for the application of that
421: behavior (simply a predicate over the state of the world), and the
422: steps that the behavior prescribes (physical actions, mental actions,
423: subgoals). At this point, many details enter the description, to
424: provide control over managing goals and behaviors. There are three kind
425: of behaviors:
426: \begin{enumerate}
427: \item \textbf{Sequential}: the steps are performed in order, and
428: failure of any step signifies the failure of the corresponding goal to
429: which the behavior is attached; success of the last step signifies
430: success of the corresponding goal.
431: \item \textbf{Concurrent}: the steps are performed in any order, but
432: again failure of any step signifies the failure of the corresponding
433: goal.
434: \item \textbf{Collection}: the steps are performed in any order, but
435: success or failure of the steps are irrelevant. When all steps have
436: succeeded or failed, the corresponding goal succeeds. 
437: \end{enumerate}
438: Subgoals steps in behaviors can moreover be annotated as \emph{persistent},
439: that is when they succeed or fail, they are not removed, but rather
440: persist as a continuing goal. Typically, top-level goals are
441: persistent. Conversely, subgoals can be annotated with a \emph{success
442: test}, a predicate over the state of the world, which gets tested
443: every time the ABT is activated. If the success test of a subgoal
444: is true, the subgoal automatically succeeds. 
445: 
446: Choosing a step to perform is by default done at random over all the
447: applicable steps in an ABT, that is all the leaves that can be either
448: executed right away or subgoals that can be expanded because a
449: behavior applies (no behavior may apply because either none has been
450: defined or no pre-condition is satisfied). Similarly, choosing a
451: behavior to perform once a subgoal step has been chosen is by default
452: done at random over all applicable behaviors. One can modify this
453: default by assigning \emph{priorities} to various subgoals. 
454: 
455: All of this is meant to evoke the kind of structure we would like to
456: express in our framework. Since Hap revolves around the notion
457: of goals, we abstractly provide a notion of goal to the framework of
458: the previous sections, where goals are for simplicity represented as
459: strings.  
460: \begin{code}
461:   \kw{datatype} goal_status = Success | Failure 
462:                        | Active | Available 
463:                        | NoSuch
464:   \kw{val} goalSet : string -> unit
465:   \kw{val} goalSucceed : string -> unit
466:   \kw{val} goalFail : string -> unit
467:   \kw{val} goalStatus : string -> goal_status
468:   \kw{val} goalClear : string -> unit
469: \end{code}
470: where \expr{goalSet} enables the given goal, such that it is
471: to be pursued by the agent (it becomes available). The functions
472: \expr{goalSucceed} and \expr{goalFail} are used to record that a goal
473: has succeeded or failed. The function \expr{goalStatus} returns the
474: status of the given goal. The status of a goal is either
475: \expr{Success} or \expr{Failure} if the goal has been recorded as
476: such, or \expr{Active} if a behavior is actively pursuing the goal,
477: but is not done with it yet. A status of \expr{Available} indicates
478: that the goal is enabled, but that no behavior is pursuing it, while a 
479: status of \expr{NoSuch} indicates that no such goal exists. The
480: function \expr{goalClear} removes a goal from the active  
481: list of goals. We define the boolean-valued helper functions
482: \expr{isAvailable} and  \expr{isDone} to check the status
483: of a goal to be respectively \expr{Available} or
484: \expr{Success}/\expr{Failure}. 
485: 
486: We interpret an ABT behavior as a rule, triggered both by the
487: pre-condition of the behavior (if present) and the apparition as
488: \expr{Available} of the goal the behavior is meant to pursue. We do
489: not worry about either sequential, collection or concurrent
490: annotations, choosing rather to let the programmer manage the steps of 
491: the behavior explicitly. Patterns quickly emerge. For instance, a behavior for goal 
492: \expr{g} triggered by a condition \expr{c} and sequentially performing 
493: subgoal \expr{g1}, action \expr{a} and subgoal \expr{g2} can be
494: interpreted as a rule:
495: \begin{code}
496:   \kw{val} beh1 = 
497:    \{cond = \kw{fn} () => \kw{if} isAvailable (\expr{g}) \kw{andalso} \expr{c} 
498:                       \kw{then} 1 \kw{else} 0,
499:     action = \kw{fn} () => 
500:                (goalSet (\expr{g1});
501:                 wait (SOME (\kw{fn} () => isDone (\expr{g1})));
502:                 \kw{case} (goalStatus  (\expr{g1}))
503:                   \kw{of} Success => (goalClear (\expr{g1});
504:                                  \expr{a};
505:                                  wait (NONE);
506:                                  goalSet (\expr{g2});
507:                                  wait (SOME (\kw{fn} () => isDone(\expr{g2})));
508:                                  \kw{case} (goalStatus (\expr{g2}))
509:                                    \kw{of} Success => (goalClear (\expr{g2});
510:                                                   goalSucceed (\expr{g}))
511:                                     | _ => (goalClear (\expr{g2});
512:                                             goalFail (\expr{g})))
513:                    | _ => (goalClear (\expr{g1});
514:                            goalFail (\expr{g})))\}
515: \end{code}
516: 
517: Similarly, the previous behavior can be implemented concurrently by
518: setting all the goals at once and waiting for all the goals to be done. 
519: \begin{code}
520:   \kw{val} beh2 = 
521:    \{cond = \kw{fn} () => \kw{if} isAvailable (\expr{g}) \kw{andalso} \expr{c} 
522:                       \kw{then} 1 \kw{else} 0,
523:     action = \kw{fn} () => 
524:                (goalSet (\expr{g1});
525:                 goalSet (\expr{g2});
526:                 \expr{a};
527:                 wait (SOME (\kw{fn} () => isDone (\expr{g1}) \kw{andalso} isDone (\expr{g2})));
528:                 \kw{case} (goalStatus (\expr{g1}),goalStatus (\expr{g2}))
529:                   \kw{of} (Success,Success) => (goalClear (\expr{g1});
530:                                            goalClear (\expr{g2});
531:                                            goalSucceed (\expr{g}))
532:                    | (_,_) => (goalClear (\expr{g1});
533:                                goalClear (\expr{g2});
534:                                goalFail (\expr{g})))\}
535: \end{code}
536: 
537: By virtue of the \expr{andalso} in the first waiting condition of
538: \expr{beh2}, the conditions must all be true for the system to proceed 
539: at that point. Although it does sequentializes the testing of the
540: conditions, this is not an issue given our current framework since
541: the rule is resumed when all the conditions are satisfied. It may
542: however become an issue if we attempt to optimize the satisfaction of
543: the conditions. Persistence of goals can be implemented by
544: clearing them and setting them again, while goal success tests can be
545: wrapped inside the \expr{wait} condition for that goal.
546: 
547: One difficulty of this approach, immediately noticeable from the above 
548: code, is
549: that it is very error-prone, even if it is much more
550: flexible than ABTs. In effect, we have to implement the handling
551: of the goals explicitly, for every single behavior. However, the
552: flexibility of
553: first-class rules and first-class functions allows us to easily
554: generate such rules from a declarative description of the intended
555: behavior. Consider a type \expr{behavior} that describes a behavior
556: declaratively: 
557: \begin{code}
558:   \kw{type} behavior = \{goal : string,
559:                    precond : (unit -> bool) option,
560:                    kind : behavior_kind,
561:                    steps : behavior_step list\}
562: 
563:   \kw{datatype} behavior_kind = Sequential | Concurrent
564:   \kw{datatype} behavior_step = Subgoal \kw{of} string
565:                          | Action \kw{of} unit -> bool
566: \end{code}
567: (For simplicity, we drop the collective kind of behavior, its handling 
568: similar enough to the concurrent one to not cause a problem). We can
569: describe the previous two behaviors \expr{beh1} and \expr{beh2} as
570: follow (this time using behavior parameterization!):
571: \begin{code}
572:   \kw{fun} beh (k) = \{goal = \expr{g},
573:                  precond = SOME (\kw{fn} () => \expr{c}),
574:                  kind = k,
575:                  steps = [Subgoal (\expr{g1}),
576:                           Action (\kw{fn} () => \expr{a}),
577:                           Subgoal (\expr{g2})]\}
578:   \kw{val} beh1 = beh (Sequential)
579:   \kw{val} beh2 = beh (Concurrent)
580: \end{code}
581: 
582: We can then interpret such descriptions in our framework, by a
583: function \expr{behaviorRule}, which takes a description of type
584: \expr{behavior} and returns a rule of type \expr{rule}. The
585: implementation of \expr{behaviorRule} is simply a question of writing
586: a rule whose action is an interpreter for lists of
587: \expr{behavior\_step}. For the truly interested, the code for
588: \expr{behaviorRule} is given in Figure \ref{f:behaviorrule}.
589: 
590: \begin{figure}
591: \hrule
592: \medskip
593: \begin{code}
594:   \kw{fun} behaviorRule \{goal,precond,kind,steps\} = \kw{let}
595:     \kw{fun} split [] = ([],[])
596:       | split (x::xs) = \kw{let}
597:           \kw{val} (sg,acts) = split (xs)
598:         \kw{in}
599:           \kw{case} x
600:             \kw{of} Subgoal (g) => (g::sg,acts)
601:              | Action (a) => (sg,a::acts)
602:         \kw{end}
603:     \kw{fun} cond () = \kw{if} isAvailable (goal) \kw{andalso}
604:                      (\kw{case} precond \kw{of} NONE => true
605:                                     | SOME (pc) => pc ())
606:                          \kw{then} 1 \kw{else} 0
607:   \kw{in}
608:     \kw{case} kind
609:       \kw{of} Sequential => \kw{let}
610:            \kw{fun} perform_steps [] = goalSucceed (goal)
611:              | perform_steps (Action (a)::r) = 
612:                       (a ();
613:                        wait (NONE);
614:                        perform_steps (r))
615:              | perform_steps (Subgoal (g)::r) = 
616:                       (goalSet (g);
617:                        wait (SOME (\kw{fn} () => isDone (g)));
618:                        \kw{case} (goalStatus (g))
619:                          \kw{of} Success => (goalClear (g);
620:                                         perform_steps (r))
621:                           | _ => (goalClear (g);
622:                                   goalFail (goal)))
623:          \kw{in}
624:            \{cond = cond,
625:             action = \kw{fn} () => perform_steps (steps)\}
626:          \kw{end}
627:        | Concurrent => 
628:            \{cond = cond,
629:             action = \kw{fn} () => \kw{let}
630:               \kw{val} (subgoals,actions) = split (steps)
631:             \kw{in}
632:               app goalSet subgoals;
633:               app (\kw{fn} a => a ()) actions;
634:               wait (SOME (\kw{fn} () => List.all (\kw{fn} x => x) 
635:                                             (map isDone subgoals)));
636:               \kw{if} (List.all (\kw{fn} g => \kw{case} (goalStatus (g)) 
637:                                       \kw{of} Success => true 
638:                                        | _ => false) 
639:                            subgoals)
640:                 \kw{then} (app goalClear subgoals;
641:                       goalSucceed (goal))
642:               \kw{else} (app goalClear subgoals;
643:                     goalFail (goal))
644:             \kw{end}\}
645:   \kw{end}
646: \end{code}
647: \hrule
648: \caption{Code for \expr{behaviorRule}}
649: \label{f:behaviorrule}
650: \end{figure}
651: 
652: 
653: \COMMENTOUT{
654: To help concretize this description, consider the following simplified
655: example. We model a baby with two high top-level goals,
656: \emph{enjoy-self} and \emph{not-hungry}, with \emph{not-hungry} having
657: higher priority. These top-levels goals are setup by an initial
658: behavior called \emph{setup} which installs them as a collection of
659: persistent goals. We assume a variable \emph{hunger} which is
660: increased at regular intervals to indicate that the baby is getting
661: hungry. Note that \emph{not-hungry} has a success test that
662: basically makes it succeed when hunger is 0, which should happen when
663: the baby eats. 
664: 
665: \begin{centercode}
666: val setup = Beh \{name="B-setup",
667:                  goal="main",
668:                  pre_condition=NONE,
669:                  kind=Coll,
670:                  steps = [(Subgoal \{priority=10,
671:                                     importance=2,
672:                                     name="enjoy-self",
673:                                     success_test = NONE\},
674:                            true,false,false),
675:                           (Subgoal \{priority=100,
676:                                     importance=10,
677:                                     name="not-hungry",
678:                                     success_test = SOME (fn () => (!hunger)=0)\},
679:                            true,false,false)]\}
680: \end{centercode}
681: 
682: We define a single behavior for the \emph{enjoy-self} goal, namely to
683: smile contently. This is straightforward enough: the behavior has a
684: single step, to perform a mental action of broadcasting that the baby
685: smiles to everyone (this could also have been a physical action that
686: succeeds). 
687: 
688: \begin{centercode}
689: val smile = Beh \{name="B-smile",
690:                  goal="enjoy-self",
691:                  pre_condition=NONE,
692:                  kind = Seq,
693:                  steps = [(Mental (fn () => \textit{broadcast: baby smiles}),
694:                            false,false,false)]\}
695: \end{centercode}
696: 
697: Handling hunger is slightly more complicated, but not much more. We
698: have a single behavior 
699: 
700: \begin{centercode}
701: val eat = Beh \{name="B-eat",
702:                goal="not-hungry",
703:                pre_condition=NONE,
704:                kind=Seq,
705:                steps = [(Subgoal \{priority=100,
706:                                   importance=0,
707:                                   name="unsatisfiable",
708:                                   success_test = SOME (fn () => \textit{hunger > threshold})\},
709:                          false,false,false),
710:                         (Physical (fn () => (if \textit{bottle near baby}
711:                                                then \textit{baby drinks, hunger=0, succeed}
712:                                              else \textit{baby cries, fails})),
713:                          false,true,false)]\}
714: \end{centercode}
715: 
716: Finally, we can create the ABT for the baby and the control loop to
717: drive the actions: 
718: 
719: \begin{centercode}
720: \kw{let}
721:   \kw{val} baby_abt = mk_abt \{behaviors=[setup,smile,eat],
722:                               goal="main"\}
723:   \kw{fun} loop () = (\textit{wait 2 seconds};
724:                  execute (baby_abt);
725:                  \textit{increment hunger};
726:                  loop ())
727: \kw{in}
728:   \textit{hunger = 0};
729:   loop ()
730: \kw{end}
731: \end{centercode}
732: }
733: 
734: 
735: 
736: \section{Implementation}
737: \label{s:impl}
738: 
739: The framework we have described has been implemented for the Standard
740: ML of New Jersey compiler \cite{Appel91} using the reactive library
741: described in \cite{Pucella98}. One advantage of this approach is that
742: the semantics of the system is easily derivable from the one in
743: \cite{Pucella98}. Before discussing the implementation, let us give an
744: overview of the reactive library.  
745: 
746: The library defines a type \expr{rexp} of reactive expressions, which
747: are expressions that define control points. A reactive expression is
748: created through a function \expr{rexp} that expects a \expr{unit
749: $\rightarrow$ unit} function as argument. The argument function calls
750: the function \expr{stop} to define a control point. The function \expr{react}
751: is used to take a reactive expression and to evaluate the code starting
752: from the last control point reached until the next control point is
753: reached. This is called \emph{activating} a reactive
754: expression. When a reactive expressions evaluates to a value without
755: reaching a control point, it is said to \emph{terminate}. Interesting
756: combinators can be defined to take reactive 
757: expressions and combining them. The most important of such combinators 
758: is the \expr{merge} combinator, which takes two reactive expression
759: $e_1$ and $e_2$ and creates a new reactive expression $e$ that behaves 
760: as follows: when $e$ is activated, $e_1$ and $e_2$ are activated,
761: one after the other. In effect, this interleaves the execution of
762: $e_1$ and $e_2$. The combinator \expr{loop} takes a reactive
763: expressions $e_1$ and creates a new reactive expression $e$ that
764: behaves as follows: when $e$ is activated, $e_1$ is activated. If
765: $e_1$ terminates, it is reset (the reactive expression is
766: re-instanciated) and activated again. The reactive expression
767: \expr{nothing} simply terminates immediately. 
768: 
769: A more fine-grained notion of control point is also
770: available. A reactive expression can call \expr{suspend} to suspend
771: its current execution. A \expr{suspend} acts as a \expr{stop}, except
772: that a special combinator \expr{close} is available. Given a reactive
773: expression $e_1$, \expr{close} returns a new reactive expression which 
774: behaves as follows: when $e$ is activated, it repeatedly activates
775: $e_1$ until all its reactive subexpressions have reached
776: {stop}-defined control points. This allows finer control over the
777: order of execution of the reactive subexpressions, an example of which 
778: we will see in this section. 
779: 
780: It is clear, given this description, how the library may be useful to
781: implement the details of  overlapping rules. For brevity, we assume
782: the reactive library has been  
783: bound to a structure \expr{R}. The implementation of the framework is
784: rather simple, although it is complicated by technical 
785: details and some mismatches with the underlying reactive library. We
786: define a rule set as a reactive expression, a \emph{merge} of all the
787: relevant rules.
788: \begin{code}
789:   \kw{type} rule_set = R.rexp
790:   \kw{fun} newSet () = R.nothing
791: \end{code}
792: 
793: A rule is defined as before, while adding a rule to a set of rules
794: consists of merging the reactive expression corresponding to the rule
795: in the merge of the rule set.
796: \begin{code}
797:   \kw{type} rule = \{cond : unit -> word, 
798:                action : unit -> unit\}
799: 
800:   \kw{fun} addRule rs \{cond,action\} = \kw{let}
801:     \kw{val} r = R.exp (\kw{fn} () => (condition (cond);
802:                              action ()))
803:   \kw{in}
804:     R.merge (rs,r)
805:   \kw{end}
806: 
807:   \kw{val} mkSet = foldr addRule (newSet ())
808: \end{code}
809: 
810: The function \expr{condition} terminates immediately if monitoring
811: determines that it should be fired (according to the fitness of the
812: condition), otherwise it stops to wait for another instant
813: where the condition is deemed fit. To bypass a limitation of the current reactive library,
814: which is more geared towards locally determining whether a given
815: reactive expression is allowed to continue rather than being selected
816: through a global check, we introduce a global variable to hold a list
817: of all computed fitnesses and allow the system to select the
818: ones that will execute \footnote{This does make the library non-reentrant. This could 
819: be corrected by an appropriate change to the reactive library
820: (implementing reaction-specific data, for instance), or by including a
821: notion of execution context to bind the use of the variable to a given 
822: context.}.
823: \begin{code}
824:   \kw{val} globalFitnesses = ref ([]) : (unit ref * word) list ref
825: \end{code}
826: 
827: We uniquely tag each condition being computed using a value of type
828: \expr{unit ref}, a trick commonly used in SML to get unique
829: identifiers that can be quickly compared for identity. When the
830: function \expr{condition} is encountered, the current fitness is 
831: computed and stored along with a unique identifier for the
832: condition. The reactive expression is then \emph{suspended} (not
833: stopped). This gives the other reactive expressions running \emph{in
834: parallel} a chance to evaluate their conditions. Upon resumption of the
835: suspension, each condition checks if it is allowed to continue by
836: seeing if it is listed in a list of \emph{allowed-to-continue}
837: conditions, stored in the previous \expr{globalFitnesses} variable.
838: \begin{code}
839:   \kw{fun} condition (f) = \kw{let}
840:     \kw{val} r = ref ()
841:     \kw{fun} loop () = (globalFitnesses := (r,f)::(!globalFitnesses)
842:                    suspend ();
843:                    \kw{if} (List.exists (\kw{fn} (r',_) => r=r') (!globalFitnesses))
844:                      \kw{then} ()
845:                    \kw{else} (stop (); loop ()))
846:   \kw{in}
847:     loop ()
848:   \kw{end}
849: \end{code}
850: 
851: The function \expr{wait} that implements an
852: interruption of the execution of the rule is simple:
853: \begin{code}
854:   \kw{fun} wait (NONE) = (stop (); suspend ())
855:     | wait (SOME (f)) = (stop (); condition (f))
856: \end{code}
857: (The suspend in \expr{wait (NONE)} is a technicality, needed to
858: prevent the firing of stopped rules with no conditions until all the
859: conditions of the other rules have been checked).
860: 
861: The core of the work is done in \expr{monitor}, which is in charge of activating the 
862: reactive expression corresponding to the rule set, gather the results, 
863: compute which conditions are satisfied, and resume the suspensions.
864: \begin{code}
865:   \kw{fun} monitor c rs = \kw{let}
866:     \kw{val} clearVar = R.loop (R.rexp (\kw{fn} () => (globalFitnesses := [];
867:                                               stop ())))
868:     \kw{val} r = R.loop (R.rexp (\kw{fn} () => (computeEnabled (c);
869:                                       stop ())))
870:   \kw{in}
871:     R.react (R.close (R.merge (clearVar,R.merge (rs,r))))
872:   \kw{end}
873: \end{code}
874: 
875: The above code encompasses the control flow of the monitoring process, 
876: and relies heavily on the fact that merges are deterministic. A
877: non-deterministic implementation could play with \expr{suspend} calls
878: to achieve the right order of execution: we need to make sure at every
879: instant that \expr{clearVar} is executed first, and
880: \expr{computeEnabled} is activated after all suspensions.
881: The actual rule selection is performed by \expr{computeEnabled}: 
882: \begin{code}
883:   \kw{fun} computeEnabled (AllBest) = \kw{let}
884:         \kw{fun} max (curr,[]) = curr
885:           | max (curr,(_,x)::xs) = \kw{if} x>curr \kw{then} max (x,xs) \kw{else} max (curr,xs)
886:         \kw{val} bestVal = max (1,!globalFitnesses)
887:         \kw{val} lst = List.filter (\kw{fn} (_,a) => a=bestVal) (!globalFitnesses)
888:       \kw{in} 
889:         globalFitnesses := lst
890:       \kw{end}
891:     | computeEnabled (RandBest) = \textit{(as AllBest, but return random element)}
892:     | computeEnabled (AllDownTo (v)) = \kw{let}
893:         \kw{val} lst = List.filter (\kw{fn} (_,a) => (a>=v)) (!globalFitnesses)
894:       \kw{in} 
895:         globalFitnesses := lst
896:       \kw{end}
897:     | computeEnable (RandDownTo (v)) = \textit{(as AllDownTo, but return random element)}
898: \end{code}
899: 
900: 
901: 
902: \section{Conclusion}
903: 
904: So what have we done? We have developed a general framework for
905: rule-based programming in SML, flexible enough to handle standard
906: OPS-style rules, as well as overlapping rules. The fact that rules are 
907: first-class in the framework gave us enough flexibility to
908: express Loyall's active behavior trees through an interpreter of
909: declarative behaviors. 
910: 
911: We have not worried about the efficiency of the framework. Some rather 
912: standard optimizations as found in modern rule-based systems can
913: easily be applied, although optimizations of the conditions may
914: require them to be lifted into a more structured type, such as for
915: example
916: \begin{code}
917:   \kw{datatype} condition = Basic \kw{of} unit -> word
918:                      | And \kw{of} condition list
919:                      | Or \kw{of} condition list
920:                      | Not \kw{of} condition
921:                      | ...
922: \end{code}
923: in order to allow for such things as caching of condition evaluations
924: and so on. On another note, evaluating a condition is only required
925: if the references the condition refers to have been changed, so an
926: optimized framework should maintain a list of the references used by
927: conditions and record changes accordingly. 
928: 
929: The tradeoff between generality and conciseness is not new. If we are
930: willing to restrict flexibility, we can design an appropriate surface language
931: which we can translate into this framework for execution. This is what 
932: we did for the implementation of behaviors in Section
933: \ref{s:abts}. This makes our framework a target language for the
934: interpretation of domain-specific rule-based languages. In a similar
935: way, the reactive library of \cite{Pucella98} was designed as a target 
936: language for the interpretation of domain-specific reactive
937: languages, which is the way it is being used in this paper. 
938: 
939: Using the SugarCubes framework described in \cite{Boussinot98}, we could
940: move most of the framework to Java, but SML has distinct advantages,
941: at least at first brush: we have seen how first-class rules and
942: first-class functions helped design suitable domain-specific
943: abstractions; the module system, although not used in this paper,
944: plays a crucial role in the generalization of the framework to general 
945: rules that can carry arbitrary data (not only conditions and
946: actions). The whole approach is also helped by the fact that SML
947: already implements state-based programming through the use of explicit
948: references. It will be interesting to see how much of this can be
949: carried over to Java. 
950: 
951: This frameworks, one of the first implemented using the library in
952: \cite{Pucella98}, also points to some conclusions about the reactive
953: approach. If the order in which parallel reactive expressions are
954: activated is important, then we need to be careful, appropriately
955: using \expr{suspend} calls to get the order right; this makes the 
956: resulting system brittle and the code hard to see correct. A better 
957: approach may be to allow one to explicitly control the order of
958: execution of the branches of a merge. On a related note, the reactive
959: library is geared towards determining reactive expression activation
960: locally, while we have encountered in this paper a reasonable instance 
961: where the activation decision is taken on a global level. It would be
962: interesting to find an extension of the reactive library to do that
963: cleanly, without resorting to a list of unique identifiers indicating
964: which expression is allowed to resume.
965: 
966: \COMMENTOUT{
967: \textbf{Future work.} Eventually, comparison with CML \cite{Reppy99},
968: in the style of \cite{Boussinot00}. Ensure actual reactivity. 
969: }
970: 
971: \bibliographystyle{plain}
972: \bibliography{main}
973: 
974: \end{document}
975: 
976: