;;;; ftl.scm ;;* [tags: egg] ;;* [toc:] ;;* ;;* == FTL (Function Template Library) ;;* ;;* === Authors ;;* ;;* Design by Sergei Egorov, implemented for CHICKEN by [[felix winkelmann]] ;;* ;;* === Abstract ;;* ;;* The purpose of this extension is to propose a comprehensive and easy-to-remember ;;* set of high-order procedures to be used as buiding blocks for a variety ;;* of ad-hoc sequence libraries. It is designed around the conventional ;;* notion of a sequence as an ordered set of elements represented by ;;* some enumerating algorithm or list- or vector-like data structure. ;;* ;;* === Specification ;;* ;;* All proposed high-order procedures take arguments implementing one ;;* of a few abstract "interfaces" to data that serve as parameters for ;;* the corresponding generalized algorithm; its specialized version is ;;* returned as the result. Each abstract interface provides access to ;;* opaque data via a small set of conventional operations; for ;;* example, mutable vector-like objects are characterized by a ;;* 4-tuple <length, ref, set!, make>. In order for ;;* an algorithm parameterized by these four operations to work ;;* as expected, all operations should conform to a certain set ;;* of requirements, explicitly specified for each interface. ;;* In all cases, these requirements are only as strict as needed ;;* by the present set of algorithms. As a result, both purely ;;* functional (eager and lazy) and side-effect-based implementations ;;* of interfaces can be used with most of the algorithms. ;;* ;;* To make the resulting collection of high-order procedures ;;* easier to remember and use, their names follow the naming convention ;;* established by traditional low-order sequence libraries ;;* (R5RS, SRFI-1, CommonLisp, ...) The result of run-time parameterization ;;* of a high-order procedure can be predicted by "substituting" ;;* names of parameters for their "placeholders" in the high-order ;;* procedure's name, and guessing at procedure's behavior by ;;* the resulting "low-order" name (which may be used to name ;;* the result). As a simple example, one can construct a procedure ;;* converting strings to vectors as follows: ;;* ;;* (define string->vector (%v->%mv v=string mv=vector)) ;;* ;;* Here, {{%v->%mv}} is the name of a high-order procedure and ;;* {{v=string}} and {{mv=vector}} are names of two interfaces of ;;* vector-like objects (read-only strings and mutable vectors). ;;* Performing a "mental printf" on the right-hand-side names ;;* allows one to check that the left-hand-side name is not ;;* misleading. In more complex examples, more than one such ;;* "printf" may be needed to understand the result: ;;* ;;* (%g-remove-%t->%o g=string (t=not-%t t=char-ci) o=list) ;;* ;;* Here, "mental printf" gives {{t=not-char-ci}} for the inner ;;* high-order application and {{string-remove-not-char-ci->list}} ;;* for the whole thing; the resulting peculiar procedure converts ;;* a string to a list after filtering out every character that is ;;* not {{char-ci=?}} to an explicit argument. If you ever need such a ;;* procedure, you'll probably name it something like ;;* {{string-filter-ci->list}} and use as follows: ;;* ;;* (string-filter-ci->list #\m "Metaprogramming") ;;* => (#\M #\m #\m) ;;* ;;* More realistic example on the same theme is SRFI-1's {{filter}} ;;* that can be constructed as {{list-remove-if-not->list}}: ;;* ;;* (define filter (%g-remove-%t->%o g=list t=if-not o=list)) ;;* ;;* Note, that arguments to our high-order procedures should ;;* be given in the exact order suggested by the order of {{%}}''<interface>'' ;;* placeholders and should have matching ''<interface>''{{=}}''...'' names; ;;* Scheme knows nothing of these conventions and may not provide ;;* enough error-checking to spot invalid procedural arguments ;;* before actual use of the result. ;;* ;;* === Entry format ;;* ;;* The body of the specification is organized into entries. Each entry describes ;;* one constant or procedure or a group of related constants or procedures. ;;* ;;* If ''category'' is '''constant''', ''template'' gives the name of the ;;* constant (global identifier, bound to an interface object). If ''category'' ;;* is '''procedure''', ''template'' gives a template for a call to a procedure, ;;* followed, by and the description of the returned value. We follow ;;* the RnRS convention that if an argument or result name is also ;;* the name of the type, than that argument must be of the named type. ;;* Additional conventions used in this SRFI are: ;;* ;;* ((proc arg ...) obj ...) => res procedure, returning a procedure, returning res ;;* template => res1 | res2 alternative results ;;* template => (values res ...) multiple results ;;* (proc [arg1 [arg2]]) procedure with two optional arguments (second or both can be omitted) ;;* (proc [arg1 arg2]) procedure with two optional arguments (none or both can be omitted) ;;* e, oe, t, x, g, o, a, i, li, v, mv interface objects (tuples conforming to interface specifications) ;;* p predicate/pattern/prototype object, as required by t interface ;;* src source, as required by g interface ;;* dst destination, as required by a and o interfaces ;;* out output object, as required by o interface ;;* in input object, as required by i interface ;;* lin lookahead input object, as required by li interface ;;* res accumulator/output result (a, o interfaces) ;;* vec vector-like object, as required by v interface ;;* mvec mutable vector-like object, as required by mv interface ;;* (sub)vector vector or vector subrange (as returned by sub) ;;* (sub)string string or string subrange (as returned by sub) ;;* ;;* === Interfaces ;;* ;;* Although there are dozens of ways to represent ;;* sequences and iterative computation, we decided to select only ;;* those closely matching traditional list/vector processing ;;* algorithms with some support for more esoteric cases involving ;;* hidden state and side effects. This SRFI does not allow for ;;* such things as backtracking and does not provide an elegant ;;* solution to the fringe comparison problem. The following 11 interfaces ;;* represent common abstractions generic enough to build ;;* most (but not all) functions defined in SRFI-1, SRFI-13, ;;* and SRFI-43 plus thousands more by combination. ;;* ;;* nterfaces have one- or two-letter abbreviated names used ;;* to form names of high-order procedures and interface implementations. ;;* Two-letter interfaces (oe, mv, li) are extensions of the corresponding ;;* one-letter ones (e, v, i). Extensions conform to all requirements ;;* of the "base" interfaces plus some extra requirements of their own. (declare (export sub)) (define (sub seq start #!optional stop) (let* ((len (seq-length seq)) (stop (or stop len)) ) (assert (fx>= start 0) "subsequence start is out of range" seq start) (assert (fx< stop len) "subsequence end is out of range" seq stop) (##sys#make-structure 'subrange seq start stop) ) ) (define (seq-length seq) (cond ((vector? seq) (vector-length seq)) ((string? seq) (string-length seq)) (else (error 'sub "not a valid sequence" seq)) ) ) (define (range x loc k) (cond ((##sys#structure? x 'subrange) (k (##sys#slot sub 1) (##sys#slot sub 2) (##sys#slot sub 3)) ) ((vector? x) (k x 0 (vector-length x)) ) ((string? x) (k x 0 (string-length x)) ) (else (error loc "invalid (sub)sequence" x)))) (define-macro (check-interface x i loc) (let ((var (gensym))) `(let ((,var ,x)) (##sys#check-structure ,var 'ftl-interface ,loc) ,@(map (lambda (i) `(assert (eq? ',i (##sys#slot ,var 1)) "not an interface of the required type" ,var ',i) ) (if (pair? i) i (list i)) ) ,var) ) ) (define-record-printer (subrange x p) (fprintf p "#" (##sys#slot x 2) (##sys#slot x 3)) ) (define-record-printer (ftl-interface x p) (fprintf p "#" (##sys#slot x 1)) ) (define-macro (interface i . vals) `(##sys#make-structure 'ftl-interface ,i ,@vals) ) ;;* ;;* ==== Equality (e) ;;* ;;* Equality interface is the simplest one in our set - it consists of a single ;;* two-argument procedure implementing an ''equivalence'' relation; that is, it must ;;* be symmetric, reflexive, and transitive. In addition, calls to the the procedure are ;;* expected to be ''referentially transparent'' (i.e. return the same result given ;;* the same arguments) and have no side effects. The rationale for this requirement is ;;* to allow algorithms to calculate equality as many times as needed and expect ;;* consistent results. ;;* ;;* Equality interfaces can be constructed and de-constructed as follows: ;;* ;;* [procedure] (e-interface eq) => e ;;* ;;* Returns a new e interface defined by ''eq'' predicate. ;;* ;;* [procedure] ((%e=? e) obj1 obj2) => boolean ;;* ;;* Returns the equality predicate component of ''e''. ;;* ;;* Common e interfaces: ;;* ;;* Interface based on predicate Category ;;* ;;* e=q eq? constant ;;* e=v eqv? constant ;;* e=l equal? constant ;;* e=number = constant ;;* e=char char=? constant ;;* e=char-ci char-ci=? constant ;;* e=string string=? constant ;;* e=string-ci string-ci=? constant (declare (export e=q e=v e=l e=number e=char e=char-ci e=string e=string-ci %e=? e-interface)) (define (e-interface p) (interface 'e p)) (define e=q (e-interface eq?)) (define e=v (e-interface eqv?)) (define e=l (e-interface equal?)) (define e=number (e-interface =)) (define e=char (e-interface char=?)) (define e=char-ci (e-interface char-ci?)) (define e=string (e-interface string=?)) (define e=string-ci (e-interface string-ci?)) (define (%e=? i) (check-interface i (e oe) '%e=?) (##sys#slot i 2) ) ;;* ;;* ==== Order and Equality (oe) ;;* ;;* Order and Equality interface is an extension of the Equality interface; in addition ;;* to the equivalence procedure, it includes a two-argument procedure implementing ;;* a ''partial ordering'' relation, compatible with the equivalence relation. This means ;;* that the ordering relation must be transitive, irreflexive, and obey the trichotomy ;;* law with regard to the equivalence relation. Calls to both procedures are expected ;;* to be ''referentially transparent''. ;;* ;;* [procedure] (oe-interface eq less) => oe ;;* ;;* Returns a new oe interface defined by ''eq'' and ''less'' ;;* predicates. ;;* ;;* [procedure] ((%oe=? oe) obj1 obj2) => boolean ;;* ;;* Returns the equality predicate component of ''oe''. ;;* ;;* [procedure] ((%oe boolean ;;* ;;* Returns the ordering predicate component of ''oe''. ;;* ;;* [procedure] ((%oe>? oe) obj1 obj2) => boolean ;;* [procedure] ((%oe<=? oe) obj1 obj2) => boolean ;;* [procedure] ((%oe>=? oe) obj1 obj2) => boolean ;;* ;;* These procedures return ordering predicates derived from ;;* primitive components of ''oe''. ;;* ;;* A ''oe'' interface may be used in any situation where an ''e'' interface would be allowed. ;;* ;;* Common oe interfaces: ;;* ;;* Interface based on predicates Category ;;* ;;* oe=number = < constant ;;* oe=char char=? char? %oe>=? %oe<=?)) (define (oe-interface e l) (interface 'oe e l)) (define (%oe=? i) (check-interface i oe '%oe=?) (##sys#slot i 2) ) (define (%oe? i) (check-interface i oe '%oe>?) (let ((=? (##sys#slot i 2)) (=? i) (check-interface i oe '%oe>?) (let ((?) (let ((=? (##sys#slot i 2)) ( x obj2 ;;* ;;* Returns the transformation function component of ''t''. ;;* ;;* Common x interfaces: ;;* ;;* Interface based on function Category ;;* ;;* x=not not constant ;;* x=abs abs constant ;;* x=add1 (lambda (v) (+ v 1)) constant ;;* x=sub1 (lambda (v) (- v 1)) constant ;;* x=car car constant ;;* x=cdr cdr constant ;;* x=integer->char integer->char constant ;;* x=char->integer char->integer constant ;;* x=upcase char-upcase constant ;;* x=downcase char-downcase constant (declare (export x-interface %x x=not x=abs x=add1 x=sub1 x=car x=cdr x=integer->char x=char->integer x=upcase x=downcase)) (define (x-interface x) (interface 'x x)) (define (%x i) (check-interface i x '%x) (##sys#slot i 1) ) (define x=not (x-interface not)) (define x=abs (x-interface abs)) (define x=add1 (x-interface add1)) (define x=sub1 (x-interface sub1)) (define x=car (x-interface car)) (define x=cdr (x-interface cdr)) (define x=integer->char (x-interface integer->char)) (define x=char->integer (x-interface char->integer)) (define x=upcase (x-interface char-upcase)) (define x=downcase (x-interface char-downcase)) ;;* ;;* ==== Test (t) ;;* ;;* Test interface is a generalization of testing methods used by search and compare ;;* algorithms such as R5RS {{memq}} and SRFI-1's {{find-tail}} and ;;* plays the role of CommonLisp's {{-if}}, {{-if-not}}, ;;* {{:test}}, and {{:test-not}} conventions. ;;* ;;* Test interface consists of a single ;;* two-argument procedure implementing some sort of test of its first "fixed" argument (an argument to ;;* a sequence algorithm) with respect to a "variable" argument (usually coming from a sequence). ;;* Calls of the test procedure are expected ;;* to be ''referentially transparent''. ;;* ;;* [procedure] (t-interface p) => t ;; ;;* Returns a new t interface defined by ''p'' (a predicate). Example: ;;* ;;* (define t=memq (t-interface memq)) ; memq matches the requirements for p ;;* ;;* [procedure] ((%t? t) fobj vobj) => boolean ;;* ;;* Returns the test predicate component of ''t''. ;;* ;;* [procedure] (t=%e e) => t ;;* ;;* Converts e interface into t via {{(t-interface (%e=? e))}} ;;* ;;* [procedure] (t=not-%t t) => t ;;* ;;* Returns a logical complement of ''t''. {{t=not-%t}} ;;* can be defined as follows: ;;* ;;* (define (t=not-%t t) ;;* (let ((t? (%t? t))) ;;* (t-interface (lambda (f v) (not (t? f v)))))) ;;* ;;* [procedure] (t=%x&%t x t) => t ;;* ;;* Returns a new ''t'' that adds an "input transformation" ;;* specified by ''x'' to its variable argument. {{t=%x&%t}} ;;* can be defined as follows: ;;* ;;* (define (t=%x&%t x t) ;;* (let ((fn (%x x)) (t? (%t? t))) ;;* (t-interface (lambda (f v) (t? f (fn v)))))) ;;* ;;* [constant] t=if ;;* ;;* {{t=if}} expects that the "fixed" argument is a predicate and ;;* applies it to the "variable" argument, returning the result of ;;* the application (or its logical complement, in case of {{t=if-not}}). ;;* They can be defined as follows: ;;* ;;* (define t=if (t-interface (lambda (f v) (f v)))) ;;* (define t=if-not (t=not-%t t=if)) ;;* ;;* Other common t interfaces: ;;* ;;* Interface based on predicate Category ;;* ;;* t=q eq? constant ;;* t=v eqv? constant ;;* t=l equal? constant ;;* t=number = constant ;;* t=char char=? constant ;;* t=char-ci char-ci=? constant ;;* t=string string=? constant ;;* t=string-ci string-ci=? constant ;;* (declare (export t-interface %t? t=%e t=not-%t t=%x&%t t=if t=if-not t=q t=v t=l t=number t=char t=char-ci t=string t=string-ci)) (define (t-interface p) (interface 't p)) (define (%t? i) (check-interface i t '%t?) (##sys#slot i 1) ) (define (t=%e e) (check-interface e (e oe) 't=%e) (interface 't (%e=? e)) ) (define (t=not-%t t) (check-interface t t 't=not-%t) (let ((t? (##sys#slot t 1))) (interface 't (lambda (f v) (not (t? f v)))))) (define (t=%x&%t x t) (check-interface x x 't=%x&%t) (check-interface t t 't=%x&%t) (let ((fn (##sys#slot x 1)) (t? (##sys#slot t 1)) ) (interface 't (lambda (f v) (t? (f (fn v))))) ) ) (define t=if (t-interface (lambda (f v) (f v)))) (define t=if-not (t=not-%t t=if)) (define t=q (t-interface eq?)) (define t=v (t-interface eqv?)) (define t=l (t-interface equal?)) (define t=number (t-interface =)) (define t=char (t-interface char=?)) (define t=char-ci (t-interface char-ci=?)) (define t=string (t-interface string=?)) (define t=string-ci (t-interface string-ci=?)) ;;* ;;* ==== Generator (g) ;;* ;;* The Generator interface captures the common notion of an algorithm producing a ;;* finite series of elements based on some initial "source". A source can be a container ;;* (in which case the generator enumerates its content), a value encapsulating initial ;;* state for an algorithm that produces alternative solutions (moves in a chess ;;* game), or any other object that can be mapped onto a sequence of values. ;;* ;;* Generators adhere to the "push" model of output: the internal state of the ;;* process of generation is implicit and need not to be reified in a ;;* first class form by rewriting of the original algorithm or via ;;* {{call/cc}}. In this SRFI, algorithms that consume the entire ;;* input sequence and fit naturally into the passive filtering/accumulation ;;* model are defined as high-order procedures over generators ;;* ({{%g}} algorithms). Formal criterion for what constitutes a ;;* "natural fit" is given in [1] (algorithm should be ''closed under cons''). ;;* ;;* The Generator interface consists of a single three-argument procedure patterned ;;* after the traditional ''fold'' family of functions (e.g.: SRFI-1's {{fold}} ;;* and {{fold-right}}): ;;* ;;* {fold ''kons'' ''knil'' ''src'') => ''klist'' ;;* ;;* The generator takes its third argument, ''src'', as its source and ;;* "folds" it by feeding each element it produces to its first argument, a ;;* {{cons}}-like binary procedure ''kons''. This procedure ;;* is applied to each subsequent element and the result of a previous such ;;* application, if it existed, or the second generator argument, ''knil''. ;;* The algorithm behind the process of generation need not to be referentially ;;* transparent, but it should not expect that applications of its second argument ;;* are referentially transparent, and it should satisfy the ''fusion law'': ;;* ;;* For all ''f, v, x, y, prime,'' and ''f', v''' such that ;;* ''prime''(''v'') = ''v''', and ;;* ''prime''(''f'' (''x, y'')) = ;;* ''f'''(''x'', ''prime''(''y'')), ;;* the following should hold for {{fold}}: ;;* ''prime''({{fold}}(''f, v'')) = ;;* {{fold}}(''f', v''') ;;* ;;* In practice, these requirements mean that the generator should treat its first two ;;* arguments as opaque and is only allowed to apply its second argument once to ;;* the same intermediate value of ''klist''. Restricting generators to ;;* single-pass mode make them compatible with non-functional ways to consume ;;* generated values, such as writing to a file. ;;* ;;* [procedure] (g-interface fold) => g ;;* ;;* Returns a new g interface defined by ''fold''. ;;* ;;* [procedure] ((%g-fold g) kons knil src) => obj ;;* ;;* Returns the ''fold'' component of ''e''. ;;* (declare (export g-interface %g-fold)) (define (g-interface f) (interface 'g f)) (define (%g-fold g) (check-interface g g '%g-fold) (##sys#slot g 1) ) ;;* Generators can be built from other, more specific interfaces: ;;* ;;* [procedure] (g=%i i) => g ;;* [procedure] (g=reverse-%i i) => g ;;* [procedure] (g=%v v) => g ;;* [procedure] (g=reverse-%v v) => g ;;* ;;* These procedures return a new g interface based on functionality available ;;* through ''i'' and ''v'' interfaces. Reverse variants are produced differently: ;;* {{g=reverse-%i}} is based on recursive (right) fold, while {{g=reverse-%v}} ;;* iterates through indices in reverse order. (declare (export g=%i g=reverse-%i g=%v g=reverse-%v)) (define (g=%i i) (check-interface i (i li) 'g=%i) (let ((rd (##sys#slot i 1))) (interface 'g (lambda (k n s) (let loop ((s s) (r n)) (call-with-values (cut rd s) (match-lambda* (() r) ((x s2) (loop s2 (k x r))) (rs (error "`i'-interface read procedure returns invalid number of results" rs)) ) ) ) ) ) ) ) (define (g=reverse-%i i) (check-interface i (i li) 'g=%i) (let ((rd (##sys#slot i 1))) (interface 'g (lambda (k n s) (let loop ((s s)) (call-with-values (cut rd s) (match-lambda* (() n) ((x s2) (k x (loop s2))) (rs (error "`i'-interface read procedure returns invalid number of results" rs)) ) ) ) ) ) ) ) (define (g=%v v) (check-interface v (v mv) 'g=%v) (interface 'g (lambda (k n s) (range s 'g=%v (lambda (s start len) (let ((ref (##sys#slot v 2)) ) (let loop ((i 0) (r n)) (if (fx>= i len) r (loop (fx+ i 1) (k (ref s i) r)) ) ) ) ) ) ) ) ) (define (g=reverse-%v v) (check-interface v (v mv) 'g=%v) (interface 'g (lambda (k n s) (range s 'g=reverse-%v (lambda (s start len) (let ((ref (##sys#slot v 2)) ) (let loop ((i (fx- len 1)) (r n)) (if (fx< i start) r (loop (fx+ i 1) (k (ref s i) r)) ) ) ) ) ) ) ) ) ;;* ;;* [procedure] (g=%g-%x g x) => g1 ;;* ;;* Returns a new g interface defined by mapping ''x'' over values ;;* produced by ''g''. This operation is known as ''fusion''. (declare (export g=%g-%x)) (define (g=%g-%x g x) (check-interface g g 'g=%g-%x) (check-interface x x 'g=%g-%x) (let ((f1 (##sys#slot g 1)) (fn (##sys#slot x 1)) ) (interface 'g (lambda (k n s) (fn (f1 k n s)) ) ) ) ) ;??? ;;*Common g interfaces with sources, description of their output, ;;* and more specific interfaces they can be built from: ;;* ;;* Interface src generates Cf. Category ;;* ;;* g=iota number numbers in [0..N) in increasing order constant ;;* g=list list elements (iterative fold) i constant ;;* g=reverse-list list elements in reverse order (recursive fold) i constant ;;* g=string (sub)string characters v constant ;;* g=reverse-string (sub)string characters in reverse order v constant ;;* g=vector (sub)vector elements v constant ;;* g=reverse-vector (sub)vector elements in reverse order v constant ;;* g=port port S-expressions (via read) i constant ;;* g=char-port port characters (via read-char) i constant ;;* g=file filename S-expressions (via read) constant ;;* g=char-file filename characters (via read-char) constant ;;* ;;* [procedure] (sub sequence start [stop]) => subrange ;;* ;;* Returns a "subrange" object, distinguishable from strings, vectors and ;;* other standard data types except procedures. Subrange objects store the ;;* arguments used to construct them and are accepted as valid arguments to ;;* vector- and string-based inputs and generators. ;;* (declare (export g=iota g=list g=reverse-list g=string g=reverse-string g=vector g=reverse-vector g=port g=char-port g=file g=char-file) ) ---------------------------------------------------------------------------------------------------- ;;*

Output (o)

;;* ;;*

The Output interface is complementary to the Generator interface - it provides the exact ;;* functionality needed to consume values produced by a generator. The initial output ;;* object is created by calling Output's constructor procedure with no arguments or an ;;* appropriate "destination" argument (copied verbatim from the invokation of a sequence ;;* algorithm). As new elements appear, they are fed to the output's ''write'' procedure, ;;* patterned after {{cons}}. When all elements are consumed, output's ''result'' ;;* procedure is called to convert the output to the appropriate final form. ;;*

;;*

Algorithms, using the Output interface, should not expect that applications of ;;* the the output's ''write'' procedure are referentially transparent, so ''write'' ;;* should not be called more than once with the same output object; the ''out'' ;;* argument to ''write'' should come from the constructor or previous call to ;;* ''write''. Restricting the use of outputs to single-pass makes it possible to ;;* model side-effect-based output processes such as writing to a file. ;;*

;;*

Outputs are "passive" in a sense that the internal state of the consumption process ;;* exists in a first-class form (an ''output object'', a.k.a. ''out'') and there ;;* is no mechanism for the output to stop the generation process before it is over (i.e. ;;* to signal the "full" condition). In this SRFI, algorithms that need direct control ;;* over one or more data consumers are defined as high-order procedures over outputs ;;* ({{%o}} algorithms). ;;*

;;* ;;*
({{o-interface}} ''create'' ''write'' ''result'') ''o''procedure
;;*

Returns a new g interface defined by component procedures. ;;*

;;* ;;*
(({{%o-create}} ''o'') [''dst'']) ''out''procedure
(({{%o-write}} ''o'') ''obj'' ''out'') ''out''procedure
(({{%o-result}} ''o'') ''out'') ''obj''procedure
;;*

These procedures return the respective components of ''o''. ;;*

;;* ;;* ;;*

Common o interfaces:

;;* ;;*
Interfacedst, defaultcollects viaresultCategory
{{o=count}}''number'', {{0}}{{(lambda (e c) (+ 1 c))}}a countconstant
{{o=sum}}''number'', {{0}}{{+}}a sumconstant
{{o=product}}''number'', {{1}}{{*}}a productconstant
{{o=min}}''number'', {{#f}}{{min}}minimum or {{#f}}constant
{{o=max}}''number'', {{#f}}{{max}}maximum or {{#f}}constant
{{o=list}}(not accepted), {{'()}}{{cons}}{{reverse}} (FIFO)constant
{{o=reverse-list}}''obj'', {{'()}}{{cons}}the list (LIFO)constant
{{o=port}}''port'', {{(current-output-port)}}{{write}}the portconstant
{{o=char-port}}''port'', {{(current-output-port)}}{{write-char}}the portconstant
{{o=file}}''filename'' (required){{write}}the (closed) portconstant
{{o=char-file}}''filename'' (required){{write-char}}the (closed) portconstant
;;* ;;* ;;*

Accumulator (a)

;;* ;;*

The Accumulator interface captures the common notion of an algorithm consuming a ;;* possibly infinite series of elements "unfolded" from some initial value and ;;* calculating its result value based on an initial state (created from an optional ;;* "destination") and this series. Accumulators may create and populate containers, ;;* fill existing containers, calculate sums, products and averages etc. ;;*

;;*

Accumulators adhere to the "pull" model of output: the internal state of the ;;* process of accumulation is implicit and need not to be reified in a ;;* first class form by rewriting of the original algorithm or via ;;* {{call/cc}}. In this SRFI, algorithms that produce a possibly ;;* infinite input sequence and fit naturally into the passive scanning/selection ;;* model are defined as high-order procedures over accumulators ;;* ({{%a}} algorithms). Formal criterion for what constitutes a ;;* "natural fit" is given in [1] (algorithm should be able to produce ;;* ''tail'' of every value it produces). ;;*

;;* ;;*

The Accumulator interface consists of a single two-or-three-argument procedure similar ;;* to the traditional ''unfold'' family of functions (cf.: SRFI-1's {{unfold}} ;;* and {{unfold-right}}): ;;*

;;* ;;*

{{(unfold}} ''dekons'' ''klist'' [''dst'']{{)}} ''result'' ;;* ;;* ;;*

;;* The accumulator is initialized by optional third argument, ''dst'', ;;* taken verbatim from the invokation of a sequence algorithm (usually, it is also ;;* the last optional argument there). It continues by unfolding its second argument ;;* ''klist'' with the help of its first argument ''dekons'', an unary procedure ;;* returning zero or two values. ''Dekons'' is first applied to the initial value ;;* of ''klist''; if it returns two values, first of them is a new element and second ;;* is a new value for ''klist''. When there are no (more) values to get, ''dekons'' ;;* returns no values. ;;*

;;* ;;*

The algorithm behind the process of accumulation need not to be referentially ;;* transparent, but it should not expect that applications of its first argument ;;* are referentially transparent, meaning that it cannot apply ''dekons'' more ;;* than once to the same value of ''klist''. Restricting accumulators to ;;* single-pass mode make them compatible with non-functional ways to produce values, ;;* such as reading from a file. This is also the rationale behind the choice ;;* of ''dekons'' over more traditional ''<knull?, kar, kdr>'' triple: ;;* ''dekons'' is able to model no-lookahead producers such as {{read}}. ;;*

;;* ;;* ;;*
({{a-interface}} ''unfold'') ''a''procedure
;;*

Returns a new a interface defined by ''unfold''. ;;*

;;* ;;*
(({{%a-unfold}} ''a'') ''dekons'' ''klist'' [''dst'']) ''res''procedure
;;*

Return the ''unfold'' component of ''a''. ;;*

;;* ;;*

"Active" Accumulators can be easily built from "passive" Output interfaces. ;;* As the outputs they are buit from, such accumulators rely on the algorithm ;;* caller to guarantee that the input series is finite. ;;*

;;* ;;*
({{a=%o}} ''o'') ''a''procedure
;;*

Convert o interface into a. ;;*

;;* ;;*

Accumulators can be built from mutable vector interfaces:

;;* ;;*
({{a=%mv}} ''mv'') ''a''procedure
({{a=reverse-%mv}} ''mv'') ''a''procedure
({{a=%mv!}} ''mv'') ''a''procedure
({{a=reverse-%mv!}} ''mv'') ''a''procedure
;;*

These procedures return a new a interface based on functionality available ;;* through the ''mv'' interface. Non-destructive accumulators, built by ;;* {{a=%mv}} and {{a=reverse-%mv}}, do not support ''dst'' arguments; ;;* they build new vectors of the appropriate type from scratch. Destructive ({{!}}) ;;* variants require the caller to supply the ''dst'' argument of the appropriate type ;;* (''vec'') to be filled with incoming elements while there is space and elements ;;* are coming. Reverse variants iterate through indices in reverse order. ;;*

;;* ;;*
({{a=%x-%a}} ''x'' ''a'') a1procedure
;;*

Returns a new a interface defined by mapping ''x'' over values ;;* consumed by ''a''. This operation is known as ''fusion''. ;;*

;;* ;;*
Interfacedst, defaultaccumulates viaresultCategory
{{a=count}}''number'', {{0}}{{(lambda (e c) (+ 1 c))}}a countconstant
{{a=sum}}''number'', {{0}}{{+}}a sumconstant
{{a=product}}''number'', {{1}}{{*}}a productconstant
{{a=and}}''boolean'', {{#t}}{{and}}; stops earlylogical "and"constant
{{a=or}}''boolean'', {{#f}}{{or}}; stops earlylogical "or"constant
{{a=min}}''number'', {{#f}}{{min}}minimum or {{#f}}constant
{{a=max}}''number'', {{#f}}{{max}}maximum or {{#f}}constant
{{a=list}}(not accepted), {{'()}}{{cons}}{{reverse}} (FIFO)constant
{{a=reverse-list}}''obj'', {{'()}}{{cons}}the list (LIFO)constant
{{a=port}}''port'', {{(current-output-port)}}{{write}}the portconstant
{{a=char-port}}''port'', {{(current-output-port)}}{{write-char}}the portconstant
{{a=file}}''filename'' (required){{write}}the (closed) portconstant
{{a=char-file}}''filename'' (required){{write-char}}the (closed) portconstant
;;* ;;*

Prospective a interfaces [special interest?]: ;;*

;;* ;;*
Interfacedst, defaultaccumulates viaresultCategory
{{a=gcd}}''integer'', {{0}}gcdgcdconstant
{{a=lcm}}''integer'', {{1}}lcmlcmconstant
{{a=mean}}''number'', {{#f}}sum, count, dividemean or {{#f}}constant
{{a=quadratic-mean}}''number'', {{#f}}sum squares, count, divide, sqrtrms (root-mean-square) or {{#f}}constant
{{a=geometric-mean}}''number'', {{#f}}multiply, count, expt 1/countmean or {{#f}}constant
;;* ;;*

Input (i)

;;* ;;*

The Input interface is complementary to the Accumulator interface - it provides the exact ;;* functionality needed to produce values consumed by an accumulator. Inputs are created ;;* explicitly by providing a first-class ''input object'' (a.k.a. ''in'') as an ;;* argument to a sequence algorithm. Elements are extracted by calling the input's ''read'' ;;* procedure compatible with Accumulator's ''dekons'' (receives an input object and ;;* returns zero values or two values: element and new input object). Input ends when ;;* the invokation of input's ''read'' procedure returns zero values. ;;*

;;* Inputs are "passive" in a sense that the internal state of the production process ;;* exists in a first-class form (an ''input object''). Inputs need not to be read ;;* to the end; many algorithms consume input elements until some condition is satisfied, ;;* and return the "rest" input object to the caller. In this SRFI, algorithms that need ;;* direct control over one or more data producers are defined as high-order procedures ;;* over inputs ({{%i}} algorithms). ;;*

;;* ;;*
({{i-interface}} ''read'') ''i''procedure
;;*

Returns a new i interface defined by ''read''. ;;*

;;* ;;*
(({{%i-read}} ''i'') ''in'') ;;* {{(values)}} or {{(values}} ''obj'' ''in''{{)}}procedure
;;*

Return the ''read'' component of ''i''. ;;*

;;* ;;*

Inputs can be built from vector interfaces:

;;* ;;*
({{i=%v}} ''v'') ''i''procedure
({{i=reverse-%v}} ''mv'') ''i''procedure
;;*

These procedures return a new i interface based on functionality available ;;* through the ''v'' interface. Reverse variant iterates through indices in reverse order. ;;*

;;* ;;*

Common i interfaces:

;;* ;;*
InterfaceenumeratesCategory
{{i=list}}a list, ''in''s are cdrsconstant
{{i=vector}}a (sub)vector; ''in''s are subrangesconstant
{{i=reverse-vector}}a (sub)vector, backwards ; ''in''s are subrangesconstant
{{i=string}}a (sub)string; ''in''s are subrangesconstant
{{i=reverse-string}}a (sub)string, backwards; ''in''s are subrangesconstant
{{i=port}}a port, via {{read}}; ''in'' is always the port itselfconstant
{{i=char-port}}a port, via {{read-char}}; ''in'' is always the port itselfconstant
;;* ;;* ;;*

Lookahead Input (li)

;;* ;;*

The Lookahead Input interface is an extension of the Input interface. ;;* It provides two additional procedures - ''empty?'' and ''peek''. ;;* The ''empty?'' procedure should be a predicate returning non-{{#f}} ;;* when next call to ''read'' is guaranteed to return an element, and ;;* returning {{#f}} when ''read'' is guaranteed to return no values. ;;* The ''peek'' procedure should accept an input object and return the first ;;* element without actually removing it from the input. ''Peek'' guarantees that ;;* the same element (in {{eqv?}} sense) will be returned by the next ;;* call to ''read''. It is possible to test a lookahead stream for emptyness ;;* and peek more than once with no intervening ''read'', and the result is ;;* guaranteed to be the same. The effect of calling ''peek'' on an empty ;;* input is undefined. ;;*

;;* ;;*

Lookahead Inputs provide functionality needed by many stop-at-element ;;* search algorithms ({{memq}} is a good example). An ability to ;;* look one element ahead is required by many parsing algoritms working ;;* on character or token streams, but not all inputs can provide it; for ;;* example, Scheme's standard S-expression parser ({{read}}) does ;;* not support checking for emptyness and one-element lookahead, while ;;* character-oriented input provides both via {{peek-char}}. ;;*

;;* ;;*

Note, that although this SRFI allows li interface objects to ;;* be "backward compatible" with requirements of i interface and ;;* used as-is wherever i interface object is expected, it does not ;;* require such compatibility and defines the {{i=%li}} procedure ;;* for explicit conversion. ;;*

;;* ;;*
({{li-interface}} ''read'' ''empty?'' ''peek'') ''li''procedure
;;*

Returns a new li interface defined by component procedures. ;;*

;;* ;;*
(({{%li-read}} ''li'') ''lin'') ;;* {{(values)}} or {{(values}} ''obj'' ''in''{{)}}procedure
(({{%li-empty?}} ''li'') ''lin'') ''boolean''procedure
(({{%li-peek}} ''li'') ''lin'') ''obj''procedure
;;*

These procedures return the respective components of ''li''. ;;*

;;* ;;*
({{i=%li}} ''li'') ''i''procedure
;;*

Converts li interface into i via ;;* {{(i-interface (%li-read}} ''li''{{))}}. If an ;;* implementation supports "backward compatibility" between li ;;* and i interfaces, this function is an identity. ;;*

;;* ;;*

Lookahead Inputs can be built from vector interfaces:

;;* ;;*
({{li=%v}} ''v'') ''li''procedure
({{li=reverse-%v}} ''mv'') ''li''procedure
;;*

These procedures return a new li interface based on functionality available ;;* through the ''v'' interface. Reverse variant iterates through indices in reverse order. ;;*

;;* ;;*

Common li interfaces:

;;* ;;*
InterfaceenumeratesCategory
{{li=list}}a list, ''in''s are cdrsconstant
{{li=vector}}a (sub)vector; ''in''s are subrangesconstant
{{li=string}}a (sub)string; ''in''s are subrangesconstant
{{li=char-port}}a port, via {{read-char}}/{{peek-char}}; ''in'' is always the port itselfconstant
;;* ;;* ;;*

Vector (v)

;;* ;;*

The Vector interface generalizes read-only finite sequences supporting ;;* random access to elements. Obvious candidates for such generalization are ;;* vectors and strings, but other possibilities like random-access files and ;;* bit-vectors exist. We will make a distinction between Vectors (implementations ;;* of v interface), vectors (all lower case, standard Scheme vectors), ;;* and ''vec''s (sequence objects, manipulated through the appropriate ;;* Vectors). ;;*

;;* ;;*

Vectors are defined by providing ''length'' and ''ref'' procedures. ;;* The ''length'' procedure accepts one argument (a sequence of the appropriate type) ;;* and should return a nonnegative exact integer, specifying the upper bound for ;;* indexing operations (valid indices go from zero to one less than upper bound). ;;* The ''ref'' operation should accept two arguments - a sequence and an exact ;;* integer in bounds, defined by ''length'', and return an element located at ;;* that index. Vectors guarantee that if ''vec''s (sequence objects) are accessed ;;* only through v interface, repeated ''ref''s to the same location ;;* will return the same object (in {{eqv?}} sense). This guarantee supports ;;* algotrithms that need to access the same location multiple times. ;;*

;;* ;;*

Vectors provide functionality needed by search algorithms requiring ;;* indexed access to the sequence (for example, {{binary-search}}). ;;* Although it is easy to build g, i, and li ;;* interfaces from an instance of v interface (and there are ;;* procedures for that), Vectors are not considered extensions of Generator ;;* or Input/Lookahead Input interfaces, because there are many ways to ;;* build "weaker" interfaces from a Vector; this SRFI specifies only ;;* one of them: enumeration in ascending order of indices. ;;*

;;* ;;*
({{v-interface}} ''length'' ''ref'') ''v''procedure
;;*

Returns a new v interface defined by component procedures. ;;*

;;* ;;*
(({{%v-length}} ''v'') ''vec'') ''n''procedure
(({{%v-ref}} ''v'') ''vec'' ''n'') ''obj''procedure
;;*

These procedures return the respective components of ''v''. ;;*

;;* ;;*

Common v interfaces:

;;* ;;*
InterfaceenumeratesCategory
{{v=vector}}a (sub)vectorconstant
{{v=string}}a (sub)stringconstant
;;* ;;* ;;*

Mutable Vector (mv)

;;* ;;*

The Mutable Vector interface is an extension of the Vector interface. ;;* It provides two additional procedures - ''set!'' and ''make''. ;;* The ''set!'' operation should accept three arguments - a sequence, an exact ;;* integer in bounds, defined by ''length'', and a new value to store at that index. ;;* The return value of ''set!'' is unspecified. The ''make'' procedure ;;* accepts a nonnegative exact integer and an optional initial value ;;* and returns a newly allocated sequence of the given length, filled with ;;* the initial value. If no initial value were given, the contents of the ;;* sequence is not specified. ;;*

;;* ;;*

In addition to Vector's guarantees, Mutable Vectors guarantee that if ''mvec''s ;;* (mutable sequence objects) are accessed only through mv interface, ;;* a ''ref'' to a location will return an object, placed there by the most ;;* recent application of ''set!'' to that location, or initial value, if no ;;* ''set!'' calls to that location were made. ;;*

;;* ;;*

Note, that although this SRFI allows mv interface objects to ;;* be "backward compatible" with requirements of v interface and ;;* used as-is wherever v interface object is expected, it does not ;;* require such compatibility and defines the {{v=%mv}} procedure ;;* for explicit conversion. ;;*

;;* ;;*
({{mv-interface}} ''length'' ''ref'' ''set!'' ''make'') ''mv''procedure
;;*

Returns a new mv interface defined by component procedures. ;;*

;;* ;;*
(({{%mv-length}} ''mv'') ''mvec'') ''n''procedure
(({{%mv-ref}} ''mv'') ''mvec'' ''n'') ''obj''procedure
(({{%mv-set!}} ''mv'') ''mvec'' ''n'' ''obj'') ''unspecified''procedure
(({{make-%mv}} ''mv'') ''n'' [''obj'']) ''mvec''procedure
;;*

These procedures return the respective components of ''mv''. ;;*

;;* ;;*
({{v=%mv}} ''mv'') ''v''procedure
;;*

Converts mv interface into v via ;;* {{(v-interface (%mv-length}} ''mv''{{) (%mv-ref}} ''mv''{{)}}. If an ;;* implementation supports "backward compatibility" between mv ;;* and v interfaces, this function is an identity. ;;*

;;* ;;*

Common mv interfaces:

;;* ;;*
InterfaceenumeratesCategory
{{mv=vector}}a (sub)vectorconstant
{{mv=string}}a (sub)stringconstant
;;* ;;* ;;*

Algorithms

;;* ;;*
{{((%oe-min oe) x y ...) => x}}procedure
{{((%oe-max oe) x y ...) => x}}procedure
;;* ;;*
{{((%v-null? v) x) => boolean}}procedure
{{((sub%mv mv) mvec i j) => mvec}}procedure
{{((%mv-copy mv) mvec) => mvec}}procedure
{{((%mv-fill! mv) mvec e)}}procedure
{{((%mv-append mv) vec ...) => vec}}procedure
{{((%v->%mv v mv) vec) => vec}}procedure
{{((%v->%mv! v mv) vec mvec)}}procedure
{{((%mv mv) obj ...) => mvec}}procedure
{{((%mv-map! v) xy...->z mvec vec ...)}}procedure
{{((%mv-reverse! v) mvec)}}procedure
;;* ;;*
{{((%g->%v g v) src) => vec}}procedure
{{((%v->%a v a) vec) => res}}procedure
;;* ;;*
{{((%mv-sort! mv) mvec less)}}procedure
{{((%mv-stable-sort! mv) mvec less)}}procedure
{{((%v-binary-%oe-search v oe) x vec) => n | #f}}procedure
;;* ;;*
{{((%a-tabulate a) n i->x [dst]) => res}}procedure
{{((%a-iota a) n [start step]) => res}}procedure
{{((make-%a a) n x [dst]) => res}}procedure
{{((%a a) x ...) => res}}procedure
{{((%a* a) x ... dst) => res}}procedure
;;* ;;*
(({{%i-next}} ''i'') ''in'') ''in''procedure
;;*

Returns a procedure that drops the first element from ''in'' and ;;* returns ''in'' containing the remaining elements. The effect of calling ;;* ''next'' on an empty input is undefined. {{%i-next}} ;;* can be defined as follows: ;;*

;;* ;;*
(define (%i-next i)
;;*   (let ((i-read (%i-read i))) 
;;*     (lambda (in)
;;*       (call-with-values 
;;*         (lambda () (i-read in))
;;*         (lambda (e in1) in1))))) ;2 values expected
;;* 
;;* ;;*
{{((%i->%a i a) src [dst]) => res}}procedure
{{((%i-map1->%a i) x->y src [dst]) => res}}procedure
{{((%i-map->%a i a) xy...->z src1 src2 ...) => res}}procedure
{{((%i-filter-map->%a i a) xy...->z? src1 src2 ...) => res}}procedure
;;* ;;* ;;* ;;*
{{((%g-length g) src) => n }}procedure
{{((%g-for-each g) x->! src)}}procedure
{{((%g-last g) src) => obj | #f}}procedure
{{((%g-count-%t g) p src) => n}}procedure
{{((%g-last-%t g) p src) => obj | #f}}procedure
;;* ;;*
{{((%g->%o g o) src [dst]) => res}}procedure
{{((%g-append->%o g o) src ...) => res}}procedure
{{((%g-append->%o* g o) src ... dst) => res}}procedure
{{((%g->%o/%g-splicing g o g1) src [dst]) => res}}procedure
{{((%g-map1->%o g o) x->y src [dst]) => res}}procedure
{{((%g-map1->o/%g-splicing g o g1) x->y* src [dst]) => res}}procedure
{{((%g-remove-%t->%o g t o) p src [dst]) => res}}procedure
{{((%g-partition-%t->%o+%o g t o o2) p src [dst1 dst2]) => (values res1 res2)}}procedure
{{((%g-filter-map1->%o g o) x->y? src [dst]) => res}}procedure
{{((%g-substitute-%t->%o g t o) new p src [dst]) => res}}procedure
;;* ;;*
{{((%i-andmap-%t i t) p src) => obj | #f}}procedure
{{((%i-ormap-%t i t) p src) => obj | #f}}procedure
{{((%i-andmap i) xy...->b src1 src2 ...) => obj | #f}}procedure
{{((%i-ormap i) xy...->b src1 src2 ...) => obj | #f}}procedure
{{((%i-tail i) src n) => tail}}procedure
{{((%i-ref i) src n) => obj}}procedure
;;* ;;*
{{((%i-take->%a i a) src n [dst]) => res}}procedure
{{((%i-take->%a+tail i a) src n [dst]) => (values res tail)}}procedure
{{((sub%i->%a i a) src from to [dst]) => res}}procedure
;;* ;;*
{{((%i-find-%t i) p src) => x | #f}}procedure
{{((%li-member-%t li t) p src) => lin | #f }}procedure
{{((%li-drop-%t li t) p src) => lin }}procedure
{{((%li-position-%t li t) p src) => n | #f}}procedure
{{((%li-mismatch-%e li e) p src1 src2) => n | #f}}procedure
{{((%li-mismatch li) xy...->b src1 src2 ...) => n | #f}}procedure
;;* ;;*
{{((%li-take-%t->%a li a t) p src [dst]) => res}}procedure
{{((%li-take-%t->%a+tail li a t) p src [dst]) => (values res tail)}}procedure
{{((%li-take-map->%a li a) x->y src [dst]) => res}}procedure
{{((%li-take-map->%a+tail li a) x->y src [dst]) => (values res tail)}}procedure