\documentclass[12pt]{article}

\title{Agent Scheme}
\author{Alik Widge}
\date{\today}

\newcommand{\bold}[1]{{\bfseries #1}}
\newcommand{\ital}[1]{\textit{#1}}

\begin{document}

\begin{titlepage}
  \maketitle

  \begin{center}
    Submission for the 1999 Kemeny Computing Competition

    Individual System Design Project
  \end{center}
\end{titlepage}


\section{Overview}
\label{overview}

Agent Scheme is a set of extensions to the functional programming
language Scheme. It allows programs written in Scheme to interface
with other autonomous programs via the D'Agents architecture. The
D'Agents architecture provides a framework for inter-agent
communication, checkpointing and migration of programs mid-execution,
and regulation of agent activities. The Agent Scheme system is a
complete Scheme interpreter which is linked to the D'Agents common
core code. The heart of the system is the agent encoding/decoding
engine, which can capture arbitrary Scheme programs mid-execution,
linearize them for transmission across a network to a potentially
different architecture, and reassemble them to continue execution on
their new host.

A critical feature of agent systems is {\em autonomy} --- agents must
be able to act without direct human control and even make decisions on 
behalf of their owner. Agent Scheme was created in order to interest
members of the artificial intelligence community in agent
technologies. Scheme is one of the favorite languages of AI
researchers due to its combination of simple syntax with powerful
programming paradigms. Agent Scheme has also performed extremely well
in initial network benchmarks. It is my belief that this new language
interpreter will allow the creation of more powerful and useful agent
programs than have previously been possible.


\section{Users' Instructions}
\label{instructions}

The D'Agents framework is defined in terms of a single server
(implemented and maintained by Bob Gray) which has access to a number
of modified language interpreters. Tcl was the first language to be
supported; within the past few years, support for Java and Python has
been added, and Scheme now joins this family. We have chosen to use
only interpreted languages because of their inherent ability to run
well on many platforms. Attempting to add agent functionality to
compiled languages would require supporting many different instruction 
sets, which is an infeasible task. Providing support for a single
interpreter written in C is much simpler.

The package \begin{verbatim}
  ~alik/lightning/agent-scheme/scheme-agent.tar.gz\end{verbatim} contains the 2.1
version of the D'Agents server and the current version of the Agent
Scheme interpreter. The system is designed for use under Linux, and
has been verified to work correctly on the Pentiums in 005. Owing to
the security mechanisms that have been defined for agents, it will be
necessary to install your own copy of the system. To do so:

\begin{enumerate}
\item Copy the tarfile to the desired directory.

\item Unzip and untar it.

\item Edit the following files. In each file, if you scroll down, you
  will come to each task in the order in which they are enumerated.
  
  \begin{enumerate}
  \item \verb+Makefile+
    \begin{itemize}
    \item Set the TCP/IP port to some value which you expect to be 
      unused.
    \item Set the PGP key directory. This generally only mean
      updating the first part of the path to point to your
      directory structure.
          
    \item Set the log directory, data directory, server directory,
      username, groupname, and local machine.

    \item Do NOT set the admin keyname.

    \item Set the variable PREFIX. If you wish, you may continue
      on and adjust compiler flags, but this is not needed.
    \end{itemize}

  \item Login files
    \begin{itemize}
    \item Environment variable LD\_LIBRARY\_PATH must point to
      {\ttfamily build/lib/} in the directory where you are
      installing
      the
      system.
    \end{itemize}


  \item \verb+agentd/access/agent.languages+
    \begin{itemize}
    \item Set the executable for STATE-SCHEME to point to
      \verb+build/bin/agent-scheme+ under your chosen
      directory.
    \end{itemize}
    
  \item \verb+agentd/access/agentd.conf+
    \begin{itemize}
    \item Set pathname.
    \item Turn encryption off for these tests.
    \item Add yourself as an AllowedUser.
    \item Add the desired test machines as
      AllowedMachines. (The system is currently set up for
      chocorua and garfield).
    \end{itemize}

            
  \item \verb+agentd/server/start\_agentd+
    \begin{itemize}
    \item Reset directory references to point to the proper
      place.
    \item The -nodaemon flag may be deleted if you wish the
      server to run as a background process, but this is not
      recommended.
    \end{itemize}
    
  \end{enumerate}
        
\item make target \verb+scheme\_install+ to install the complete
  system.
\end{enumerate}
      
With an agent system installed, you now have the complete Agent Scheme
programming language at your disposal. Agent Scheme is a complete R4RS
Scheme with one partial exception, continuations. The examples given
in
R4RS will all work, but extremely complicated programs which depend on 
continuations may behave unexpectedly. (To date, I have not
encountered such a program, but it may exist.) In addition to the
standard Scheme primitives and special forms, I have added the
following functions which ``put the Agent in Agent Scheme''.



  It is helpful to begin by designating some notation. The notation
  \ital{name} designates a required parameter; [name] designates an
  optional one. \ital{\#name} indicates an integer; \ital{``name''} indicates a
  string; \ital{(name)} is a Scheme list, and \ital{name} is an ordinary Scheme
  symbol. Other types of arguments are sufficiently rare that a
  notation is not required.

  A very common parameter is the ``agent ID''. This is a three-element 
  list of the form (``machine'' ``agent name'' \#numeric-id), uniquely
  identifying an agent within the existing network of servers. The
  machine name and either a symbolic agent name or a numeric agent ID
  are required in order to locate the desired agent.

  A standard ``failure'' return value has been established for all
  Agent Scheme functions. If a list is expected, the null list is
  returned upon failure. If any value may be returned, boolean false
  will be returned upon failure. 

  Agent functions generally return boolean true upon successful
  completion. Where an exception to this rule is made, it will be
  explicitly mentioned.


  Agent Scheme adds one special form:

\begin{tabbing}
  \underline{special form:}\=\ \ \bold{agent-begin} \ital{(bindings)}
  \ital{body} \\
  \\
  \> \ital{bindings} should have the form \\
  \> (\ital{''server''} [my-name] [\#delay]) \\
  \\
  \> designating the server with which to register the agent (usually \\
 \> ``localhost''), a name to which the new agent's ID triple will be \\
 \> bound (defaults to ``my-agent-id'') and an optional maximum delay \\
 \> time before failure is returned. (The default delay time for all \\
 \>Agent Scheme commands is 15 seconds.) \\
 \\
 \> \ital{body} is an arbitrary list of Scheme expressions. These are \\
 \> evaluated sequentially. Once all expressions have been \\
 \> evaluated, the agent ceases to be registered with the server \\
 \> and the value of the last expression is returned.
\end{tabbing}            

\bold{agent-begin} is designed to work with the block structure of
Scheme. In order to make the system easier for programmers who have
previously worked with Agent Tcl or Agent Java, the commands

\begin{tabbing}
  \bold{agent-begin!} \ital{``server''} [\#delay]\\
  \\
  and \\
  \\
  \bold{agent-end!} [\#delay]\\
  \\
  are defined. \bold{agent-begin!} registers an agent with the central \\
  server; \mbox{\bold{agent-end!}} unregisters it.\\
  \\
  \\
  \bold{agent-name} \ital{``name''} [\#delay]\\
  \\
  will register an agent with the given symbolic name so that other \\
  agents  may henceforth refer to it by that name.\\
  \\
  \\
  \bold{agent-send} \ital{(ID)} [\#code] \ital{``message''} [\#delay] \\
  \\
  sends a message to the specified agent. The code is an arbitrary \\
  integer. Traditionally, a code of 0 is neutral, 1 indicates an \\
  error of some kind, and 2 is an indication that the original \\
  message was not plain text, but a textual encoding of a Scheme \\
  object. (The encoding feature is temporarily disabled due to a \\
  bug in the generic D'Agents core which I am waiting for Bob Gray \\
  to fix.)
  \\
  \\
  \bold{agent-receive} [\#delay] \\
  \\
  receives a message sent via \bold{agent-send.} A message is a list\\
  \\
  (\ital{(ID)} \ital{(security)} \ital{\#code} \ital{``message''})\\
  \\
  where \ital{ID} designates the sender, \ital{code} and \ital{message} are the \\
  arguments to \bold{agent-send} and \ital{security} is a list\\
  \\
  (\ital{owner-keyname}\=\ \ital{machine-keyname} \ital{agent-authenticated(boolean)} \\
  \> \ital{machine-authenticated(boolean)} \ital{owner-signed(boolean)})\\
  \\
  which provides various information about the PGP-based security \\
  features provided with D'Agents. These features, although fully \\
  supported by Agent Scheme, are not really relevant to the work \\
  I did, and thus will not be discussed here. Consult the D'Agents \\
  documentation at \verb+http://agent.cs.dartmouth.edu+ for more information.\\
  \\
  \\
  There also exist functions \bold{agent-event} and
  \bold{agent-getevent} \\
  which are analogous to \bold{agent-send} and \bold{agent-receive} in function. \\
  The only difference is that the optional integer parameter
  \ital{code} is \\
  replaced by a required string parameter \ital{``tag''}. \\
  \\
  \\
  \bold{agent-submit} \ital{``machine''} \ital{function} [(args)]
  [\#delay] \\
  \\
  is in many ways the most important function in the Agent Scheme \\
  system. \bold{agent-submit} is the function which allows an agent to \\
  migrate by spawning another agent on an arbitrary server. (The \\
  server may refuse to host the new agent, and may place \\
  restrictions on the new agent's behavior; these issues are covered
  \\
  in the general D'Agents literature.) \ital{function} may be any \\
  procedure, and \ital{args} may be any list of Scheme objects. \\
  \bold{agent-submit} returns the ID of the new agent, so that the \\
  parent may communicate and give instructions to its child. When \\
  the child terminates, a message will be sent (to be read via a \\
  call to \bold{agent-receive}) which contains the result of the \\
  last expression evaluated by the child. The code on this message \\
  is always 2. If an agent is properly designed, it may submit its \\
  own main procedure and immediately exit, thus effectively \\
  migrating all its code and data to the target machine.\\
  \\
  After a submit, the standard input, output, and error ports are \\
  re-established for the agent. If the agent had any other ports
  open,\\
  they will have been closed. To make these ports usable, the command \\
  \verb=(reopen-input-port port)= or \verb=(reopen-output-port port)=
  must \\
  be used. \\
  \\
  \\
  \bold{agent-set-encryption!} \ital{bool}\\
  \bold{agent-set-signatures!} \ital{bool}\\
  \\
  These two functions enable two of the security features mentioned \\
  above --- using PGP encryption to prevent agent code from being \\
  altered in transit, and using digital signatures to verify who \\
  began the execution of an agent. In this way, it is possible to \\
  be confident that agents will not take harmful actions and hold \\
  their executor accountable if harm does occur. \\
  \\
  \\
  \bold{agent-force} \ital{(ID)} [\#delay]\\
  \\
  forces an agent to exit immediately. Generally, an agent can only \\
  execute this on its own children; other agents may be given force \\
  privileges by a server administrator.\\
  \\
  \\
  \bold{agent-info} \ital{(ID)} [\#delay]\\
  \\
  returns a large amount of information about the specified agent. The 
  \\
  return value is a list of
  \\
  (\ital{(ID)}\=\ \ital{``machine''} \ital{(security)}
  \ital{agent-pid} \ital{message-handler-pid} \\
  \> \ital{num-messages} \ital{num-meetings} \ital{num-events})\\
  \\
  The process IDs are primarily useful for debugging purposes, as are
  \\
  the fields which provide information on the number of messages,
  events, \\
  and meetings (see below) which the subject has pending.\\
  \\
  \\
  \\
  \bold{agent-sleep} \ital{\#seconds}\\
  \\
  causes an agent to become inactive for the specified number of
  seconds.\\
  \\
  \\
  \bold{agent-status} \ital{(ID)} \ital{event-boolean} [\#delay]\\
  \\
  inquires if the specified agent exists. If it does, the Scheme
  symbol \\
  \ital{exists} is returned. \ital{error} is returned in the case of
  an error.\\
  The boolean argument, if true, will cause the server to send a
  notification event \\
  with tag ``exists:foo'' when agent ``foo'' registers on the
  network. Obviously, \\
  if the ID contains no symbolic name, no notification event can be
  generated.\\
  The symbols \ital{will-notify} and \ital{will-not-notify} are
  returned if the agent does not exist;\\
  which symbol depends on whether
  notification will be sent.\\
  \\
  \\
  We now come to the ``meeting'' series of functions, which establish
  semi-\\
  permanent connections between mutually consenting agents. A meeting
  can \\
  be \= in any of
  several states,
  each indicated by a symbolic code. The codes are:\\
  \\
  \>\ital{requesting}: the meeting has been requested. \\
  \>\ital{accepting}: we have accepted the meeting, but the remote
  server has not confirmed.\\
  \>\ital{connecting}: the meeting has been accepted; the server is
  creating the connection. \\
  \>\ital{requested}: we have requested a meeting, but the remote
  agent has not replied.\\
  \>\ital{complete}: the meeting is established.\\
  \>\ital{rejected}: the remote agent does not wish to meet with
  us. \\
  \>\ital{failed}: a communication error occurred.\\
  \>\ital{terminated}: the meeting has been ended by one of the parties.\\
  \>\ital{broken}: the remote agent has probably died. \\
  \>\ital{rejecting}: we are in the process of rejecting this
  request.\\
  \>\ital{does-not-exist}: this meeting does not exist.\\
  \>\ital{message-ready}: there is a message pending on this
  connection.\\
  \>\ital{file-ready}: there is a receivable file on this connection.\\
  \>\ital{no-change}: nothing has changed since the last request for data.\\
  \\
  A meeting is represented to the agent as an integer identifier, or
  {\em handle}.
  \\
  \\
  \bold{agent-meet} \ital{(ID)} [\#delay]\\
  \bold{agent-request-meeting} \ital{(ID)} [\#delay]\\
  \\
  Both functions request a meeting with the specified
  agent. \bold{agent-meet} \\
  will block until \ital{delay} or the remote agent accepts. Both
  return 
  the handle\\
  for the new meeting, or -1 on failure.\\
  \\
  \\
  \bold{agent-get-meeting} [\#delay]\\
  \\
  will get a meeting request which has been queued by the server for
  this agent.\\
  It returns a list\\
  \\
  (\ital{\#handle} \ital{(ID)} (\ital{``remote-machine-name''
    ``remote-machine-port''}))\\
  \\
  \\
  \bold{agent-accept} \ital{\#handle} [\#delay]\\
  \bold{agent-accept-meeting} \ital{\#handle} [\#delay]\\
  \\
  Both functions accept the meeting request which is represented by
  the supplied\\
  meeting handle. As with \bold{agent-meet}, \bold{agent-accept} is a
  version which blocks\\
  until the remote server confirms the connection.\\
  \\
  \\
  \bold{agent-reject-meeting} \ital{\#handle} [\#delay]\\
  \\
  rejects the specified meeting request.
  \\
  \\
  \bold{agent-meeting-send} \ital{\#handle} \ital{\#code}
  \ital{``message''}\\
  \bold{agent-meeting-receive} \ital{\#handle} [\#delay]\\
  \\
  These functions are analogous to \bold{agent-send} and
  \bold{agent-receive},\\
  but operate on meeting handles. \bold{agent-meeting-send} is unable
  to handle \\
  arbitrary delays due to a minor inconsistency in the generic
  D'Agents core code.\\
  \\
  \\
  \bold{agent-close-meeting} \ital{\#handle}\\
  \\
  terminates the meeting. No delay parameter is possible because this
  function \\
  always returns immediately.\\
  \\
  \\
  \bold{agent-select} \ital{(requests-bool messages-bool events-bool)} 
  \ital{(handles)} [\#delay]\\
  \\
  \bold{agent-select} is similar in intent to the select() system
  call. It allows \\
  an agent to easily get information about various things that may be
  waiting for \\
  its attention. The return value mirrors the function parameters ---
  the list of\\
  booleans, as input, indicates which of the three things we are
  interested in \\
  knowing about. The returned list indicates which of those things are 
  pending. \\
  The list of meeting handles is replaced by a list of status symbols, 
  one for\\
  each handle which was provided. In this way, an agent can easily
  monitor \\
  many connections at once.
  \\
  \\
  \bold{agent-meeting-send-file} \ital{\#handle}
  \ital{file-input-port} \ital{\#num-bytes}\\
  \\
  transmits the specified number of bytes from the specified file
  through the \\
  specified meeting. The port must be a standard Scheme file input
  port. Agent \\
  Scheme provides an extension which allows the use of strings as
  Scheme ports,\\
  but these cannot be made compatible with the D'Agents filesystem
  interface.\\
  \\
  \\
  \bold{agent-meeting-get-file} \ital{\#handle} \ital{output-port}
  [\#delay]\\
  \\
  will receive the bytes sent by \bold{agent-meeting-send-file}. The
  output port\\
  here {\em is} permitted to be a string
  port. \bold{agent-meeting-get-file}
  will return\\
  the number of bytes read from the connection, or -1 on
  error. \\
  \\
  \\
  \bold{agent-get-latest-error}\\
  \\
  is provided to help agents handle errors. It will return the latest
  error which the \\
  agent system encountered as a list of\\
  \\
  (numeric-code short-string long-string)\\
  \\
  where the long-string is human-readable and the other two fields are 
  primarily \\
  designed for reading by programs only.
  \\
  \\
  In addition to the agent functions, I have provided several useful
  extensions to\\
  the Scheme programming language:
  \\
  \\
  \bold{file-size-in-bytes} \ital{``filename''}\\
  \\
  returns the size of the specified file in bytes.
  \\
  \\
  \bold{timed-apply} \ital{procedure} [(args)]\\
  \\
  applies \ital{procedure} to the arguments (if any were given),
  timing \\
  the execution. The return value is a two-element list ({\em not} a
  pair) of\\
  (\ital{time} \ital{procedure-result}).
  \\
  \\
  \bold{rand} \ital{\#upper-limit}\\
  \\
  provides a random integer that is less than the upper limit. This
  number is\\
  generally not cryptography-quality randomness, being implemented in
  terms of\\
  the C library function rand(), but is still often useful.
\end{tabbing}


I have provided a small test suite which allows one to create some
small agents, perform simple tests of the system, and obtain system
benchmarks. The files are:
\begin{enumerate}
\item {\ttfamily agent-test.scm}
  \begin{description}
  \item[test-encoding] Tests the basic object-encodings used by
    \bold{agent-submit}.
  \item[test-struct-encoding] Tests the encoding procedures on the
    object extensions defined by libscheme.
  \item[send-test \ital{message}] Tests the sending of \ital{message} to
    \bold{recv-test}.
  \item[recv-test] Receives the message sent by \bold{send-test}.
  \item[event-test \ital{tag} \ital{event}] Tests the sending of the
    specified event to \bold{getevent-test}.
  \item[getevent-test] Receives the event sent by \bold{event-test}.
  \end{description}
  \vspace{\parskip}
\item{\ttfamily remote-fact.scm} \hspace{\fill}
  
  This is the classical agent demo. Entering {\ttfamily (do-test
    "machine" x)}
  will compute the
  factorial of \ital{x} by submitting the computation to
  \ital{machine}, then print the result.

\item{\ttfamily benchmark.scm} \hspace{\fill}

Contains three testers,
  \bold{submit-test}, \bold{message-test}, and
  \bold{meeting-test}. Each takes a single integer argument which
  specifies the number of times to perform the benchmark. For stable
  results, I suggest at least 100 iterations. Entering {\ttfamily
    (set! target "machine")} will set the machine with which the
  local host attempts to communicate. The message and meeting
  benchmarks are based around a 16-byte message; evaluating {\ttfamily 
    (set! test-string-length x)} (where \ital{x} is any integer) will
  change this parameter.
\end{enumerate}

At this point, instructions on how to actually {\em use} all these
wonderful functions and scripts are in order. Assuming that you
followed the directions above, the Agent Scheme interpreter is now in
{\ttfamily build/bin/agent-scheme}. (I will assume, for convenience,
that all commands are issued from within the agent directory.) This
interpreter may be run on its own; any number of files may be provided 
on the command line, and will be read into the environment before
interactive execution begins. However, in order to make use of agent
functionality, a server must be running. There must be a server
running on every machine to be used in the test, and all servers must
be using the same port. To start a server, execute {\ttfamily
  agentd/server/start\_agentd}. All the agent test scripts are set to
communicate with ``localhost'' by default, so this is sufficient to
run the tests. However, to verify performance across the network, it
will be necessary to start multiple server processes, one per machine.


\section{Technical Description}
\label{description}

\newcommand{\submit}{\bold{agent-submit}}

Most of the Agent Scheme system is built by combining modular
components. The D'Agents server and core agent functionality are both
written as generic C/C++ code which is meant to interface with an
arbitrary set of languages. The Agent Scheme interpreter, in addition
to drawing upon this core library, is based around the public domain
libscheme system. libscheme was only ever released as version 0.5, and 
thus I have had to make some modifications to it in order to produce a 
full working Scheme. For example, tail-recursion (an essential
requirement for anything that wants to call itself Scheme) was utterly 
absent. Implementing this was a great deal of work, but has made the
libscheme system into something useful in its own right. The reason
for choosing libscheme as a base interpreter was that, despite its
incompleteness, libscheme was designed to be extended, much as the
D'Agents software was. libscheme follows the Tcl model --- a
programmer can write a procedure as a C function with a specific type
signature, register that function with a global symbol table, and then 
compile an interpreter which now includes the new primitive function
or special form.

This extension mechanism allowed the majority of the Agent Scheme
system to be written in about a month. Before I began work on the
project, the base system had been Kali Scheme, a strange and complex
mass of code which was never meant to be heavily modified. The current 
system has clean, easy-to-understand code, which I credit mostly to a
good design decision.

While most of the Agent Scheme functions are wrappers around calls to
the D'Agents core, there is one notable exception:
\bold{agent-submit}. Since the whole point of an agent system is
migratory code, it makes sense that the function which implements
migration is really the heart of the system. In order to implement
\submit , it was necessary to devise a mapping from arbitrary Scheme
programs to a platform-independent network-transmissible
encoding. The design and implementation of that encoding is the most
critical part of Agent Scheme.

For most data objects (numbers, strings, lists of objects)
it is easy to see how
they might be linearized, transmitted, and reassembled. However, we
cannot stop at migrating data --- we need migratory code. This means
that I needed the ability to examine a Scheme procedure, linearize
its code, and also include any procedures and variables on which the
first procedure depended. (This is a marked change from the Agent Tcl
system, in which the user was required to specify all variables and
procedures which would be needed on the remote system.) Here, one of
Scheme's most important properties comes into play --- procedures are
first-class objects in Scheme. There is no fundamental distinction
between functions and data. A function is stored internally in the
Scheme list representation, so that any list could theoretically be
executable as code.

In order to linearize a function call for submission, Agent Scheme
encodes by recursive descent. The function is scanned for references
to other procedures and variables. These are then joined with the
actual function call into a Scheme list, which is passed to the
encoding routines. The encoders iterate over the structure, adding
each object into a dynamically growing buffer. Each object may be
divided into one or more sections. For example, a list is divided into 
its car and cdr fields. Each section is stored separately, and
sections are delimited by ASCII NUL, the only character which is not
found in the middle of valid Scheme data.

There are additional subtleties. Since Scheme is a fundamentally
recursive language, it was necessary to avoid traversing loops in the
pointer structure repeatedly. Thus, once an object has been encoded a
single time, all further encodings of that object are done by
reference. When everything we can reach is marked such that we can
reference it, we know that we have encoded all portions of the code
structure which could possibly matter. Using references also has
the benefit of
saving space in the encoded string.

Scheme also has dynamic scoping. Two functions may reference the same
variable name, but have it be bound to very different values. Thus, in 
addition to capturing code and data, it is necessary to encode the
structure of the Scheme stack at the time of submission. To do this,
we assign each stack frame a unique identifier at submit-time. Each
variable can then be combined with a reference to its particular stack 
frame, and the stack can be reconstructed on the remote machine. (This 
is complicated by the fact that a frame may have been removed from the 
stack, but still be pointed to by a procedure object. The Scheme stack 
is highly branched.)

A final complication is the libscheme object system. libscheme objects 
are simple collections of data with named fields. (On the other hand,
since Scheme data are also Scheme procedures, the object system allows 
for member functions of an object.) When a new object type is defined, 
its type must be bound into the global symbol table, as must the
functions which access its fields. From the perspective of a routine
which encodes arbitrary data, these objects present a challenge. The
fields can be easily stored, but it will be necessary to replicate the 
type and accessor binding on the remote system. This is nontrivial,
since the only way to locate the accessor functions would be a search
through the entire reachable memory space. However, the
variable-capture routines mentioned earlier provide a cleaner
solution. By capturing all bound symbols within a function and then
recursing upon those symbols, it is possible to determine the entire
portion of the code and data space which is reachable from the
submitted function call. If the accessor functions are never called,
they will not be needed. If they {\em are} called, then they will be
reached and automatically included into the encoding in the process of 
preparing the complete submit call. Thus, all that remains is to
include instructions to bind the new type's name in the remote symbol
table.

On the receiving end, the linear data stream is reassembled by
essentially reversing the process. The embedded NUL characters make it 
easy for C functions to grab the next structural element out of the
buffer. The process of reconstructing the objects and functions
themselves is not much more complex than the process of encoding
them. Resolving references, however, can be hairy. I have chosen the
simple expedient of maintaining a temporary lookup table into which
references are entered as they are decoded. When a reference to a
not-yet-decoded object is encountered, a placeholder object is
constructed and entered into a global list of backreferences. Thus,
after the buffer is decoded, a second pass over the pointer tree is
required to re-establish linkages, bind user-defined types, and
resolve dangling references. There is also a non-crititcal but useful
computation which can be performed. During encoding, it is trivial
to measure the number of
bytes decoded. If the length of the encoded agent is saved into the
initial encoded string, we thus have a simple method of verifying
accurate and complete transmission of an agent. If there is a
discrepancy in encoding vs. decoding, it is safe to say that the agent 
has somehow been modified or damaged in transit.

Obviously, this is an extremely high-level description. However, I do
not feel that it is worthwhile to delve into exact pseudocode for
these methods.
Much of the difficulty in creating the encoding system is a matter of
being certain to follow all pointers, keep track of what has and has
not been encoded, and capture the necessary information for execution
without sending more data than is necessary.
If the process is
unclear, a visit to the debugging facilities of the encoder may
help. Simply enter \verb+(agent-set-encoder-debug! #t)+ into the
interpreter, then perform a submit. (Something small is recommended.)
It would also be wise to have the agent server running with the
-nodaemon flag set (which it is by default) --- this will allow you to 
visualize the decoding process.

Agent Scheme does not shine due to brilliant new encoding algorithms
or fundamentally new ideas. Agent Scheme stands out because it
achieves an intricate and nontrivial task while retaining a clean,
modular design and a small memory footprint. The system is also
extremely fast. Using the {\ttfamily benchmark.scm} functions, Agent
Scheme has been measured to be at least half again as fast as Agent
Tcl. Further tests of the system's speed are underway, which will pit
it in direct competition with Agent Tcl and Agent Java on a variety of 
tasks.

\section{Evaluation}
\label{evaluation}

I personally consider Agent Scheme an excellent example of how a
powerful piece of software may be built by careful combination of
modules and simple design. As previously stressed, the code is highly
extensible --- adding a new primitive takes about five minutes. Agent
Scheme also has an advantage in that it is linked against a
garbage-collected Scheme intepreter. Thus, by using garbage-collected
memory allocation, it is able to avoid many of the common memory bugs
which traditionally plague large C/C++ programs.

Another important feature of Agent Scheme is its ability to interface
with other agent languages. Scheme is very good for planning and
symbolic logic, but lacks facilities for effective text-processing or
graphics. By sending messages to an appropriately-written Java or
Tcl/Tk agent, one could create a heterogeneous distributed application 
which used the best of each programming paradigm.

Since I knew the lifetime of Agent Scheme would extend beyond my time
at Dartmouth, I have designed the system so that it will be easy for
my successors to maintain. The code is extensively commented and
logically split into files and modules. Interfaces are kept as simple
as possible between modules. In short, I feel that this is a good
demonstration of how to build a large system in C/C++.

The value of a programming language is roughly 
measurable by how many people use it. Agent Scheme has not yet been
publically released, so I cannot comment on the user base. However,
there is a known interest within the AI community in agent
technologies, and we have had many inquiries about when Agent Scheme
would be available. My prediction is that once Agent Scheme is
released, we will see a significant step forward in the capabilities
of agents, as existing reasoning programs become easy to port to the
D'Agents framework. I have also attached a letter from Assistant
Professor Daniela Rus, my supervisor on the Agent Scheme project,
which provides her thoughts on the usefulness of the system.

\end{document}


% LocalWords:  Kemeny D'Agents checkpointing Tcl alik gz Pentiums tarfile Unzip
% LocalWords:  untar IP PGP username groupname admin keyname LD IBRARY ATH lib
% LocalWords:  agentd conf AllowedUser AllowedMachines chocorua garfield gentd
% LocalWords:  nodaemon nstall RS ame umeric elay localhost getevent args bool
% LocalWords:  pid num IDs econds foo andle um filesystem rand pper scm struct
% LocalWords:  libscheme recv cdr NUL backreferences vs pseudocode intepreter
% LocalWords:  Tk
