

                                                                  producing

    MACRO
PRODUCING OUTPUT-LIST INPUT-LIST {DECLARATION}* {FORM}*             [Macro]

    Package
    series

    DESCRIPTION

PRODUCING computes and returns a group of series and non-series outputs
given a group of series and non-series inputs.  The key feature of
PRODUCING is that some or all of the series inputs and outputs can be
processed in an off-line way.  To support this, the processing in the body
(consisting of the FORMS) is performed from the perspective of generators
and gatherers (see below).  Each series input is converted to a generator
before being used in the body.  Each series output is associated with a
gatherer in the body.

The OUTPUT-LIST has the same syntax as the binding list of a LET.  The
names of the variables must be distinct from each other and from the names
of the variables in the INPUT-LIST.  If there are N variables in the
OUTPUT-LIST, PRODUCING computes N outputs.  There must be at least one
output variable.  The variables act as the names for the outputs and can be
used in either of two ways.  First, if an output variable has a value
associated with it in the OUTPUT-LIST, then the variable is treated as
holding a non-series value.  The variable is initialized to the indicated
value and can be used in any way desired in the body. The eventual output
value is whatever value is in the variable when the execution of the body
terminates.  Second, if an output variable does not have a value associated
with it in the OUTPUT-LIST, the variable is given as its value a gatherer
that collects elements.  The only valid way to use the variable in the body
is in a call on NEXT-OUT.  The output returned is a series containing these
elements.  If the body never terminates, this series is unbounded.

The INPUT-LIST also has the same syntax as the binding list of a LET.
The names of the variables must be distinct from each other and the names
of the variables in the OUTPUT-LIST.  The values can be series or
non-series.  If the value is not explicitly specified, it defaults to NIL.
The variables act logically both as inputs and state variables and can be
used in one of two ways.  First, if an input variable is associated with a
non-series value, then it is given this value before the evaluation of the
body begins and can be used in any way desired in the body.   Second, if an
input variable is associated with a series, then the variable is given a
generator corresponding to this series as its initial value.  The only
valid way to use the variable in the body is in a call on NEXT-IN.

There can be declarations at the start of the body.  However, the only
declarations allowed are IGNORE declarations, type declarations, and
PROPAGATE-ALTERABILITY declarations (see below).  In particular, it is an
error for any of the input or output variables to be special.

In conception, the body can contain arbitrary Lisp expressions.  After the
appropriate generators and gatherers have been set up, the body is executed
until it terminates.  If the body never terminates, the series outputs (if
any) are unbounded in length and the non-series outputs (if any) are never
produced.

Although easy to understand, this view of what can happen in the body
presents severe difficulties when optimizing (and even when evaluating)
series expressions that contain calls on PRODUCING.  As a result, several
limitations are imposed on the form of the body to simplify the processing
required.

The first limitation is that, exclusive of any declarations, the body must
have the form (LOOP (TAGBODY ...)).  The following example shows how
PRODUCING could be used to implement a scanner creating an unbounded series
of integers.

(PRODUCING (NUMS) ((NUM 0)) 
  (DECLARE (INTEGER NUM) (TYPE (SERIES INTEGER) NUMS)) 
  (LOOP 
    (TAGBODY 
      (SETQ NUM (1+ NUM)) 
      (NEXT-OUT NUMS NUM)))) 
 => #Z(1 2 3 4 ...)


The second limitation is that the form TERMINATE-PRODUCING must be used to
terminate the execution of the body.  Any other method of terminating the
body (with RETURN, for example) is an error.  The following example shows
how PRODUCING could be used to implement the operation of summing a series.
The function TERMINATE-PRODUCING is used to stop the computation when
NUMBERS runs out of elements.

(PRODUCING ((SUM 0)) ((NUMBERS #Z(1 2 3)) NUM)  
  (LOOP 
    (TAGBODY 
      (SETQ NUM (NEXT-IN NUMBERS (TERMINATE-PRODUCING))) 
      (SETQ SUM (+ SUM NUM))))) 
 => 6


The third limitation is that calls on NEXT-OUT associated with output
variables must appear at top level in the TAGBODY in the body.  They cannot
be nested in other forms.  In addition, an output variable can be the
destination of at most one call on NEXT-OUT and if it is the destination
of a NEXT-OUT, it cannot be used in any other way.

If the call on NEXT-OUT for a given output appears in the final part of the
TAGBODY in the body, after everything other than other calls on NEXT-OUT,
then the output is an on-line output---a new value is written on every
cycle of the body.  Otherwise the output is off-line.

The following example shows how PRODUCING could be used to split a series
into two parts.  Items are read in one at a time and tested.  Depending on
the test, they are written to one of two outputs.  Note the use of labels
and branches to keep the calls on NEXT-OUT at top level.  Both outputs are
off-line.  The first example above shows an on-line output.

(PRODUCING (ITEMS-1 ITEMS-2) ((ITEMS #Z(1 -2 3 -4)) ITEM) 
  (LOOP 
    (TAGBODY (SETQ ITEM (NEXT-IN ITEMS (TERMINATE-PRODUCING))) 
             (IF (NOT (PLUSP ITEM)) (GO D)) 
             (NEXT-OUT ITEMS-1 ITEM) 
             (GO F) 
      D      (NEXT-OUT ITEMS-2 ITEM) 
      F      ))) 
 => #Z(1 3) and #Z(-2 -4)


The fourth limitation is that the calls on NEXT-IN associated with an input
variable V must appear at top level in the TAGBODY in the body, nested in
assignments of the form (SETQ VAR (NEXT-IN V ...)).  They cannot be nested
in other forms.  In addition, an input variable can be the source for at
most one call on NEXT-IN and if it is the source for a NEXT-IN, it cannot
be used in any other way.

If the call on NEXT-IN for a given input has as its sole termination action
(TERMINATE-PRODUCING) and appears in the initial part of the TAGBODY in the
body, before anything other than similar calls on NEXT-IN, then the input
is an on-line input---a new value is read on every cycle of the body.
Otherwise the input is off-line.

The example below shows how PRODUCING could be used to concatenate two
series.  To start with, elements are read from the first input series.
When this runs out, a flag is set and reading begins from the second input.
Both inputs are off-line.  The example above shows an on-line input.

(PRODUCING (ITEMS) ((ITEM-1 #Z(1 2)) 
                    (ITEM-2 #Z(3 4)) 
                    (IN-2 NIL) 
                    ITEM) 
  (LOOP 
    (TAGBODY (IF IN-2 (GO D)) 
             (SETQ ITEM (NEXT-IN ITEM-1 (SETQ IN-2 T) (GO D))) 
             (GO F) 
      D      (SETQ ITEM (NEXT-IN ITEM-2 (TERMINATE-PRODUCING))) 
      F      (NEXT-OUT ITEMS ITEM)))) 
 => #Z(1 2 3 4)


     SEE ALSO
     about-series
     about-generators
     terminate-producing
     propagate-alterability

;Copyright 1989 by the Massachusetts Institute of Technology,
;Cambridge, Massachusetts.

;Permission to use, copy, modify, and distribute this software and its
;documentation for any purpose and without fee is hereby granted,
;provided that this copyright and permission notice appear in all
;copies and supporting documentation, and that the name of M.I.T. not
;be used in advertising or publicity pertaining to distribution of the
;software without specific, written prior permission. M.I.T. makes no
;representations about the suitability of this software for any
;purpose.  It is provided "as is" without express or implied warranty.

;    M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
;    ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
;    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
;    ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
;    WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
;    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
;    SOFTWARE.



