		1. SEL VERSION 2:  USER MANUAL


This document provides a brief overview of the SEL programming language.
SEL stands for Subset-Equational Language.  It is a functional programming 
language based upon equational and subset program clauses, with "strict" 
semantics and "weak" typing.  This language concept was proposed in 1987 by 
Jayaraman and Plaisted [JP87], as a way towards declarative and efficient 
set processing that is compatible with both functional and logic
programming paradigms.  It has since been discussed in several papers 
over the last few years [JN88, JP89, J91, J92, M93, OJ93, S93, J93].   
The novel features of SEL for set processing are:

(1) set pattern-matching, for concise and efficient iteration over sets;

(2) subset clauses, for defining a set by specifying what it contains;

(3) mode declarations, for specifying which set-valued functions 
    distribute over union in which arguments; and 

(4) closure functions, for defining sets through circular containment
    constraints and also for defining memo functions (dynamic programming).   

SEL Version 2 also has modest support for meta- and higher-order programming.
All these features will be clarified in the sequel.  Some familiarity with 
ML programming (especially programming with constructors and equations) and 
Prolog programming (especially lexical and I/O conventions) will be helpful
in reading this document and understanding SEL.

Section 2 gives the syntax of SEL programs and their informal semantics. 
Section 3 describes the user interface and facilities for tracing programs.
Section 4 is a guide to the programs in this directory.  The subdirectory 
Papers contains several papers (as .dvi files) pertinent to this language.


		2. SYNTAX AND INFORMAL SEMANTICS


The syntactic structure of a SEL program is as follows:

	mode <modes>.
	closure <functions>.
	<clauses>

The next five notes describe the syntactic categories <modes>, 
<functions>, <clauses>, <terms> and <expressions>.   This is followed
by a note on how equational and subset clauses are processed, including 
the computational effect of mode and closure declarations.

a.  <modes> is either the keyword 'none' or it is of the form 

	f1(<modelist>), ..., fn(<modelist>) 

    where <modelist> is a comma-separated sequence of yes's and no's.  
    A declaration
	mode none.
    means that no function distributes over union in any argument. A mode 
    entry foo(yes,no,yes) means that foo is a set-valued function with three
    arguments and it distributes over union in its first and third arguments.
    A set-valued function foo distributes over union in its nth argument iff
    f(...,X U Y,...) = f(...,X,...) U f(...,Y,...), where the nth argument
    is the one shown and all other arguments remain unchanged on both sides.
    
    NOTE:  The mode declaration line is mandatory.  But the SEL compiler 
    won't complain if you omit this line, and will quietly consume one or more 
    lines without telling you about it.   This should be fixed soon.


b.  <functions> is either the keyword 'none' or is of the form

	f1/a1, ...,fn/an

    where fi/ai means "function fi of arity ai".  A declaration
	closure none.
    means that there are no closure functions in the program.

    NOTE:  The closure declaration line is mandatory.  But the SEL compiler 
    won't complain if you omit this line, and will quietly consume one or more 
    lines without telling you about it.   This should be fixed soon.


c.  <clauses> is a sequence one or more <clause> which may take the following
    forms (all variables on the r.h.s. of a clause must appear on the l.h.s.):

	<f>          equals   <expression>.	% equational clause
	<f>(<terms>) equals   <expression>.	% equational clause

	<f>	     contains <expression>.	% subset clause
	<f>(<terms>) contains <expression>.	% subset clause

    <f> stands for a user-defined function symbol.  We first describe
    <terms> and <expression> and then return to explain how equational
    and subset clauses are processed.

d.  <terms> is a comma-separated sequence of <term> which in turn is made 
    up of constants, variables, and constructor symbols.  Prolog convention 
    is followed in writing these symbols, i.e., constants and constructors 
    start with a lowercase letter, and variables start with an uppercase 
    letter.  The "don't care" variable _ may appear only on the l.h.s. 
    of a clause.  

    The special constant phi stands for the empty set, and [] stands for
    the empty list.  The integer constants are -2, -1, 0, 1, 2, ... Boolean
    constants are true and false.  String constants are characters encased 
    within single quotes---you can't do much with SEL strings other 
    than pass them around, check them for equality, and print them out.
    Lists are written using Prolog convention, e.g., [1,abc,[1,2,3]], etc.
    Sets are written using mathematical convention, e.g. {1,abc,{2,xyz}}, etc.
    Sets, lists, and terms may be arbitrarily nested.  

    There are three special constructors:  

	(i) [H|T] stands for a list (as in Prolog) in which H is the head 
	    and T is the tail.

	(ii) {X\T} stands for a set S in which X is a member of S and 
             T = S - {X}.  Note:  {X} as a short-hand for {X\phi}.  Thus
	     it is in general possible to have multiple matches of {X\T} 
	     against a ground set S.  For example, let S = {1,2,3}.  Then,  
	     X<-1, T<-{2,3};  or  X<-2, T<-{1,3};  or  X<-3, T<-{1,2}.  

        (iii) univ(C, L) stands for a term C(T1,...,Tk) where C is a 
	      user-chosen constructor and L = [T1,...,Tk].  

    On the l.h.s. of a clause all constructors act as selectors, whereas 
    on the r.h.s. of of a clause they act as constructors.  When {X\T} 
    is used on the r.h.s. of a clause to build a set, the set T should
    be coerced into a set T' that does not contain X and the result should
    be {X} U T'---but the current system omits this coercion (a bug). 

e.  An <expression> is made up of constants, variables, constructors 
    and user-defined function symbols---the latter are those symbols
    that appear at the outermost level on the l.h.s. of clauses.  
    In addition, an if-then-else expression is available with its usual 
    semantics,  but nested if-then-else's are not supported (due to a 
    quirk in parsing them).  As noted earlier, SEL is a "strict" language, 
    and therefore adopts a call-by-reference semantics in reducing 
    (or processing) subexpressions.  That is, inner subexpressions are
    reduced before outer subexpressions.  The built-in operators and 
    functions of the language are as follows:

	arithmetic: +  -  *  /	        % infix operators on integers only
		    abs/1		% works on integers only

	relational: <  <=  >  >=  <>    % infix operators on integers only
		    eq/2		% operates on any pair of terms

	boolean: and or			% infix boolean operators

	higher-order fn:  apply/n	% first argument is a function name,
					% say f; remaining are f's arguments

	miscel: gensym/0 		% generates a new constant

	input/output: read_term/0, print_prompt/1, print/1, println/1

	    The input convention for read_term is similar to that of Prolog;
	    it should be a ground term terminated by period and new-line.
	    print_prompt/1 will accept only a string as an argument which
	    serves as input prompt for a subsequent read_term/0 operation.  

NOTE:  The SEL compiler is written in Prolog.  Therefore Prolog infix 
       operators, such as X+Y, X+Y*Z, etc. are available in writing terms 
       on the l.h.s. of clauses.  SEL's lexical convention w.r.t. blanks, 
       tabs, separators, and comments are also exactly as in Prolog.  
       (For efficiency reasons, the SEL run-time system has been written
       in C.  The intermediate instruction set is WAM-like [J92].)

f.  A user may define a function with a mixture of equational and 
    subset clauses, although it is more usual not to mix them.  Because 
    SEL is a functional language, a call to any function must have ground 
    arguments, i.e., no unbound variables in any call.  We clarify below 
    exactly how these clauses are processed.  In the following description, 
    assume that foo is not a closure function and no function distributes 
    over union in any argument position; later we relax these assumptions.
    In processing a call, say foo(<terms>), all equational clauses for foo 
    are tried before subset clauses for foo, where "try" means "an attempt 
    to match the call against the l.h.s. of the clause".  

    i. Trying equational clauses.    More specific l.h.s. of clauses are 
       tried before more general ones (regardless of the order given by 
       the user) until the first match is obtained.  This match is used to
       instantiate and process the corresponding r.h.s., and all remaining 
       matches and clauses are ignored in processing this call.  

    ii. Trying subset clauses.   All matches against all applicable l.h.s.
        of clauses are tried, the corresponding r.h.s.'s are instantiated,
        and the union of the results taken as the result.  Subset clauses
        are tried in the order given.  The multiple matches against the  
        l.h.s. of any one subset clause are also tried one at a time, the 
        r.h.s. being processed first before subsequent matches on the l.h.s. 
        are tried.   

    If no l.h.s. matches a call, the result will be phi---this is called 
    the emptiness-as-failure assumption.  Such a result is okay if foo is a 
    set-valued function but it may not be deemed satisfactory if foo is not 
    set-valued.  While we have not found this to be a practical problem
    thus far,  we hope to address this issue properly once we have 
    incorporated a suitable type system for SEL.

    Now we explain the computational effect of mode and closure declarations.
    
    i. Modes.  

	  Suppose foo is called in an argument position of a function baz that 
          distributes over union in this argument (assume that neither foo 
          nor baz is a closure function).  By the distribution property, 
          we have the following equality:

	  baz(...,foo(<terms>),...) = U {baz(...,{X},...) : X in foo(<terms>)}.

	  Therefore we proceed to obtain the constituent sets that make up 
	  foo(<terms>), apply baz to each such set, and take the union of 
	  these results.  The advantage of such a scheme is that one can 
	  avoid forming large intermediate sets and checks for duplicate 
	  elements.  (Note: If there are duplicates present, there is a
	  danger of over-computing, so modes should be used with some care.)
	  Terminology:  We say that foo is called in "call-one" mode when 
	  baz has the distribution property as shown.  All other calls are 
	  said to be made in "call-all" mode.  

    ii. Closure Functions. 

	   If foo is a closure function and the call foo(<terms>) occurs for 
	   the first time, foo(<terms>) is inserted into a memo-table---a 
	   run-time data-structure for recording function calls and their
	   results.  Processing continues as described earlier in "trying
	   clauses".  If, on the other hand, the call foo(<terms>) has 
	   occurred earlier, its value is just looked up in the memo-table.
	   In this sense, a closure function resembles a memo function.  The 
	   crucial difference between closure and memo functions is when
	   there is a circular dependence in calls, e.g., a call foo(<terms>)
	   results in an identical *recursive* call.   In such cases, it is 
           necessary to insert a provisional value---taken to be phi---in 
	   the memo-table for the inner call and to revise this estimate 
           and re-do the inner call if the outer call results in a different 
           value.  This process of revision and re-execution is guaranteed 
	   to reach a fixed-point if some reasonable monotonicity conditions 
	   are satisfied.  Please see [J93] for details.  NOTE:  The current 
	   implementation does not support 0-ary closure functions (a bug).



		  3. USER INTERFACE, ERRORS, AND TRACING 



Below is a sample session with the SEL System (Compiler + Run-time System)
to illustrate its use.   

____________________________________________________________________________
> sel2				% invoke the SEL System
SEL Version 2

| S- sel perms.			% compile a file called perms
Reading SEL file "perms"...
[perms/1, distr/2]		% listing of user-defined functions in perms

| S- help.			% help!
SELshell commands:

exit             Quit SELshell
help             This menu
notrall          Stop tracing all functions
pl Goal          Execute Prolog goal
plread File      Read the Prolog file File
resel            Reread the current SEL file
sel File         Read the SEL file File
?sel             Display the name of the current SEL file
trall            Trace all functions
trclear          Clear trace list and cancel tracing
troff            Disable tracing (but do not cancel the trace list)
tron             Enable tracing (using the current trace list)
tr Func/Arity    Trace the SEL function Func (add it to current trace list)
untr Func/Arity  Remove the function Func from the trace list
?tr              List the traced functions

| S- perms({1,2,3}).		% a sample top-level expression
{[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,2,1],[3,1,2]}

| S- exit.			% how to get out of SEL
Exiting SEL.

>
___________________________________________________________________________

As noted earlier, to compile a file invoke the sel command.  The file
specification is in general a string, e.g., '../../MyDir/clever.program',  
but quotes are needed only when special characters appear in the file 
specification.  To recompile the same file, type resel.  To conclude 
a session, type exit.  Currently, the SEL system allows the user to
execute functions from the most recently compiled file only;  functions
from more than one file cannot coexist in the SEL system.  We expect that
future releases will overcome this limitation.

Syntax error messages follow Prolog convention, since we make use of
Prolog for lexical and syntactic analysis.  The same convention also 
applies for input errors during read_term.   Note that, if you use a function 
foo in a file but fail to define it, or mis-spell a function as foo, the 
name foo simply defaults to being a constructor or constant (depending
upon its arity) and no syntax error is raised.  Runtime error messages are 
brief; most of them are concerned with built-in functions receiving 
improperly-typed arguments.  When an infinite loop occurs during execution, 
you need to type control-C to escape to the underlying Prolog system, then 
abort the computation, and then get back into the SEL shell by typing shell.

Any user-defined function may be invoked at the top-level, i.e., upon
receiving the SEL prompt.   Nested function invocations are also permitted.

To detect logical errors in programming, there are basically two modes 
of tracing functions:

	a. exhaustive tracing:  To trace every user-defined function, type 
		trall.   To temporarily stop tracing, type troff.  To resume 
		tracing, type tron.  To stop tracing, type notrall.

	b. selective tracing:  To trace a subset of functions, first
		type tron  and then tr for each function to be traced.
		To temporarily stop tracing, type troff.  To resume 
		tracing, type tron.  To add/remove functions from the
		trace list, invoke the tr/untr commands.  To find out the 
		current trace list, type ?tr.  To stop tracing, type trclear.


Note that the current system does not allow you to step through the 
execution of a program.

Finally, note that SEL will create three files called, 'datafile',
'outfile' and 'goalfile' in the current directory.  These files contain 
the intermediate code for the most recently compiled program and the 
most recently executed top-level expression.  This code is not very 
readable,  but you can invoke the 'pl printcode' command to the see the 
intermediate code of the program in terms of more mnemonic instruction 
names.  Paper [J92] should help in understanding these instructions.


		4. A GUIDE TO THE SAMPLE PROGRAMS IN THE DIRECTORY


A novice SEL programmer would find it useful to read the programs and 
traces in this directory in the following order.  For more examples
see the directory ~bharat/SEL2.


prime and prime.trace

   This program illustrates equational clauses, numeric and relational
   operators, and elementary list processing.  The trace in prime.trace 
   shows that inner calls in the body of prime are done before outer calls.
   This is a consequence of "strict" (call-by-reference) semantics.

   Note the convention in indenting programs;   this convention will be 
   followed in the remaining programs.  As another convention, the top-level 
   function is assumed to be the first function in the file, but one may 
   invoke any user-defined function at the SEL-shell prompt.


qsort

   This is another example of equational clauses and list processing.  Note 
   the use of the user-chosen constructor 'pair' in forming partitions.  
   All function symbols other than built-ins and those that appear at the 
   outermost position on the l.h.s. of clauses are considered constructors.
   

setops and setops.trace

    File setops shows uses of the subset assertion and set terms.  A 
    transcript of a session using these functions is in file setops.trace.
    Note that the term {X\_} on the l.h.s. of a subset clause effectively
    specifies iteration over the elements of a set.  The use of the
    remainder-set is illustrated by the second clause for permutations;
    this definition also illustrates a recursive subset clause.

    The use of set terms on the l.h.s. of *equational* clauses
    is illustrated by the definitions of member, powerset, and size.
    In such uses, the first match against the first applicable clause 
    is selected and all other matches and clauses are ignored---this is 
    precisely the effect needed in these definitions.  Contrast this with 
    the use of set terms on the l.h.s. of *subset* clauses;  there, all 
    matches against all applicable subset clauses must be considered.  
  
   (For *equational* clauses, ideally the SEL compiler should check that
    the result is independent of the selected match in case there is more
    than one.  This is known as the "confluence modulo equivalence" problem,
    a generally undecidable problem.  We expect future releases to incorporate 
    such checks when programs obey certain properties.)

gensquare, gensquare.trace, gensquare2, and gensquare2.trace

    The programs in gensquare and gensquare2 (and their respective traces)
    help to clarify the effect of mode declarations.  The trace of the
    program gensquare shows the behavior without any mode information.
    By declaring 
	mode square(yes).
    in program gensquare2, we effectively obtain a producer-consumer-like
    (or stream-like) computation over set elements.  We say that gen is
    called in call-one mode since it produces one element at a time.
    Note that there are N calls to square in gensquare2 but only one
    in gensquare.  This point is further elaborated in the perms example.

    It is noteworthy that the definition for gen can produce all elements
    at once or one element at a time depending upon how it is called.
    Reference [J92] explains the program transformations that make this 
    happen.

perms, perms.trace, perms2, and perms2.trace

    The programs in perms and perms2 (and their respective traces)
    also help contrast the difference between the call-one and call-all
    modes.  This example shows that the call-one mode of invoking 
    perms(T)---in the second clause of perms---results in exponentially
    many more calls to distr, each call having a singleton set as the 
    second argument.  Still, the overheads of duplicates checking 
    dominates that of function calls, and the call-one mode is overall
    preferable.  Paper J92 discusses this point in some detail, using 
    performance figures obtained from SEL Version 1.

maxflow

    This program makes sophisticated use of set patterns, subset clauses,
    mode and closure declarations to achieve a clear and concise formulation
    of the maximum flow through a weighted graph, using the maxflow-mincut
    theorem.
    
fib, fib.trace, perms3, perms3.trace

    The fib and perms3 programs illustrate the use of closure declarations
    in order to obtain the benefits of memoization.  The file fib contains 
    the usual double-recursive definition to compute the Nth fibonacci number.
    Since the function fib in this file is declared to be a closure function, 
    its execution takes linear time, as can be seen from fib.trace.  The 
    perms3 program shows how to speed up the basic perms definition using 
    memoization.  The trace in perms.trace should be self-explanatory.

    Note: SEL Version 2 does not support the invocation of a closure function 
    in the call-one mode.  Thus, declaring mode distr(no,yes) will have 
    no effect when closure perms/1 is also declared.
 
short

    The shortest distance between a vertex pair in a graph is a classic 
    example of a dynamic programming problem.  SEL allows you to write
    the algorithm in a clear recursive manner.  By declaring short/3
    as a closure function, we obtain the same order of efficiency as the
    corresponding iterative ("bottom-up") algorithm---thanks to memoization.   


reach and reach.trace

    The fib, perms3, and short examples all make use of memoization.  But
    in these programs, recursive function calls progressively take smaller
    arguments---although identical calls occur in different dynamic contexts.
    The natural recursive formulation for the set of reachable nodes in a
    graph reveals the need to detect cyclic recursive calls.  This is the
    simplest example (in this directory) where a provisional value--phi--for 
    an inner recursive call must be inserted into the memo-table, and this
    value is to be updated as the computation proceeds.  Refer to the file
    reach.trace to understand how this works.
    

strongcc

    This program finds the strongly connected components of a graph, and
    is a natural extension of the reach program.


inout and inout.session

    This is a more substantial illustration of updating memo-tables and
    re-doing function calls.  The program defines quite directly the
    data-flow equations for the "reaching definitions" of a program 
    flowgraph---see Principles of Compiler Design, Aho and Ullman, 1977.
    The file inout.session is annotated to show that the data-flow equations
    are indeed solved correctly.
    
eliza and eliza.session

    A simple version of the "doctor" program, due to Weizenbaum.  An
    abbreviated script is used, and is adapted from his original paper. 
    This program illustrates the use the I/O primitives for user
    interaction (see functions shrink, doc, and printlistln).  The rest
    of the program is essentially list processing and makes good use
    of equations and pattern-matching.  The script is organized
    using multiple subset clauses.

compose

    This program defines the composition of two substitutions, a basic
    operation in logical inference.  It makes essential use of the univ 
    constructor in order to decompose and synthesize arbitrary terms.  
    This is a simple illustration of meta-programming.  Note that univ 
    acts as a selector on the l.h.s. of a clause and as a constructor on 
    the r.h.s.

miniSEL and lazyeqn

    These two programs provide more substantial illustrations of meta-
    programming in SEL.  miniSEL contains an interpreter for the core 
    of SEL without closure functions,  while lazy is a program for
    lazy reduction of equational clauses.   By tracing the function
    clause/1, you may observe that the meta-interpreter is working
    correctly.   

higher

    This program illustrates use of the apply primitive for simple
    higher-order programming.   In combination with univ, this program
    combines meta- and higher-order programming.  The definition of
    map illustrates the case of a function defined with a mixture of 
    equational and subset clauses.


			BUG REPORTS


Notices of any observed bugs are gratefully accepted.  Send e-mail to
bharat@cs.buffalo.edu, giving a copy of the offended program and the error
message.   

The compiler is known to give segmentation faults after many re-compilations 
in one session.  In this case, just exit SEL, start a new session, 
and re-compile.  


			ACKNOWLEDGMENTS


SEL Version 2 was implemented by KyongHee Moon, a doctoral student at
SUNY-Buffalo.   The user interface (SELShell) is the contribution of Andrew 
Beers.  Undergraduates Andrew Beers and Erik Ray used the system extensively 
in 1992 and offered valuable feedback.  The maxflow, reach, and short
programs are due to doctoral students Kannan Govindarajan, Mauricio 
Osorio, and Terry Wilmarth respectively.  


			  FOOTNOTES


1. SEL Version 1 was completed in 1988 by Anil Nair, now at Quintus.
   It was meant to be a "proof of concept", and had many limitations w.r.t. 
   pattern-matching, modes, constructors, etc.  It also did not support 
   closure functions, a substantial language extension. 

2. The successor of SEL is SuRE---for Subset, Equational and Relational
   Language.  SuRE supports relational (i.e., Prolog-like) clauses.  
   It also permits set terms in relations---hence also set unification---
   conditional equational and subset clauses, and lazy enumeration of sets.
   Kyonghee Moon is currently working on an implementation of SuRE.
 


			ANNOTATED BIBLIOGRAPHY


[BJ93] Closure Functions and Static Program Analysis, A. Beers and 
       B. Jayaraman, to be submitted for publication, 1993.  (This paper
       shows how SEL's closure functions can be used to carry out various 
       flow analyses, including an example of how to statically detect 
       whether a SEL function possesses the distribution-over-union property.)

[JP87] Functional Programming with Sets, by B. Jayaraman and D. A. 
       Plaisted,  In 3rd Int'l Func. Prog. & Comp. Arch. conference,  
       pp. 194-210, Portland, 1987. (Introduces subset clauses,
       set patterns, and distribution over union.)

[JN88] Subset Logic Programming: Application and Implementation,
       B. Jayaraman and Anil Nair, In Fifth Int'l Logic Programming 
       Conference, pp. 848-859, Seattle, 1988.  (Introduces the idea
       of compiling set terms with WAM-like instructions.)

[JP89] Programming with Equations, Subsets and Relations, B. Jayaraman 
       and D.A. Plaisted, In North American Logic Programming Conference, 
       Cleveland, October 1989, pp.~1051-1068.  (Introduces closure functions
       as well as the Subset-Relational Language or SRL paradigm.)

[J90a] Subset-Equational Programming in Intelligent Decision Systems,
       B. Jayaraman, Invited paper for a special issue of the
       Journal of Computers and Mathematics with Applications,
       volume 20, pp. 40-63.  (Shows the use of SEL in Forward
       and Backward chaining expert systems.  See Papers/decision.dvi)  

[J90b] Towards a Broader Basis for Logic Programming, B. Jayaraman,
       Proc. Joint U.S.-Japan Workshop on Parallel Knowledge-based 
       Systems and Logic Programming,  K. Furukawa and R.M. Keller 
       (eds.), Tokyo, September 1990.  (Introduces the SuRE language,
       which is an integration of SEL and SRL.)

[J91] The SuRE Programming Framework, B. Jayaraman, TR 91-011, Department 
      of Computer Science, SUNY-Buffalo, July 1991.  (More on the SuRE
      language.   See Papers/SuRE.dvi.)

[J92] Implementation of Subset-Equational Programs, B. Jayaraman, 
      Journal of Logic Programming, 11:299-324, April 1992.  (Elaboration
      of [JN88].  Shows how set matching and subset assertions can be 
      compiled by WAM-like instructions.  Also discusses distribution 
      over union and its effect on performance.  See Papers/jlp92.dvi.)

[J93] Semantics of Subset Logic Languages, Devashis Jana,
      Ph.D. Dissertation, Dept. of Computer Science, SUNY-Buffalo,
      forthcoming 1993.  (Provides logical foundations for SEL and SuRE.)

[M93] Static Analysis and Implementation of Subset Logic Languages,
      Kyonghee Moon, Ph.D. Dissertation	Proposal, SUNY-Buffalo, May 1993.
      (Outlines research issues in the compilation and execution of the
       SEL and SuRE languages.)

[N88]  Compilation of Subset Logic Programs, Anil Nair, M.S. Thesis, 
       UNC, Chapel Hill, September 1988. (Served as Manual for SEL 
       Version 1. See ../SEL1 for this paper.)

[JJ93] Set Constructors, Finite Sets, and Unification, D. Jana and 
       B. Jayaraman, to be submitted for publication, 1993.  (Describes
       the Set Unification operation over SEL-like sets.  See 
       Papers/setunif.dvi.)

[OJ93] Subset Assertions and Negation As Faiure, M. Osorio and B. Jayaraman,  
       submitted for publication, April 1993.  (Describes memoization and
       reexecution is some detail, along with model-theoretic semantics.
       See Papers/subset-NAF.dvi.)
	
[S91] Set Representations in a Subset-Equational Language, Giancarlo
      Succi, M.S. Thesis, Dept. of Computer Science, SUNY-Buffalo,
      January 1991.  (Considers different set representations for SEL, 
      as in the SETL language.  Contact charmi@dist.unige.it for a copy.)

[S93] La Compilazione Di Linguiaggi Dichirativi Basati Su Insiemi Per
      Architetture Parallele,  Giancarlo Succi, Ph.D. dissertation,
      U. of Genova, February 1993.  (Describes a data parallel
      implementation of SEL on the Connection Machine CM-2. Contact 
      charmi@dist.unige.it for a copy.)
