[heresy-devel] Heresy lazy-list classes and subclasses (no-html resend)
Matt Lamari
matthew.lamari at gmail.com
Mon Mar 31 08:06:34 UTC 2008
(sorry about the html in the last post)
This post will refer to Heresy version 0.0.3, although the lazy-list
classes themselves did not vary from 0.0.1->0.0.3
At present, there are naming conventions that may not be perfect for the
situation (reflecting an evolution more than a destination).
It bears mentioning that the lazy-list class types were deliberately not
documented in the API - and that they currently represent performance
optimization hints (for memory or CPU) to code for various functions
that accept lazy-lists.
At the root of the lazy-list class hierarchy is *lazy-list*. In
essence, it contains a function reference that, when called, returns the
first value + the function for the next link. (In 0.0.3 this has
expanded to 2 functions, each of which returns a /traversal-result/
struct; but that's the basic idea). This has currently proven
sufficient for both fixed-container sequences and lazy/infinite
sequences. All lazy-lists support this functionality without exception,
and are acceptable to any of the functions in the library that will
accept a list.
*lazy-list-list-based* - a lazy-list representing a common-lisp "proper
list", based upon conses, nil-terminated - and the list itself is stored
in slot /list-head/. Code that creates a lazy-list will tend to create
this when the input is a proper list. Code that accepts a list will
often typecase to this, short-circuiting the more involved calls of the
lazy-list view of the data structure in favor of using the /list-head/.
These can be found by searching /heresy.lisp/ for /lazy-list-list-based/
- /foldl// is a good example, it skips a rather involved operation upon
the lazy-list in favor of a call to /common-lisp:reduce/.
*lazy-list-known-empty* is of marginal utility; but represents a
guaranteed empty list. (Note - a list may, upon traversal, have no
contents - it is not required that a list be of this type just because
it's empty, this is only a hint for certain situations where it is known
to be true).
*lazy-list-with-persistence *and *lazy-list-with-some-persistence* - the
distinction represents a judgement call between levels of lazy-list that
do not warrant caching. The former basically represents either a direct
connection to a fixed container or subset of same (or a cached
/read-point/, which, once evaluated, keeps its values), the latter may
represent a lazy-list deemed to be a very close link to the former.
*lazy-list-with-some-persistence* is used as a cue to functions such as
/memoized//, so that they can avoid the redundancy of caching something
that the lazy-list's reference will keep alive via a fixed container or
another cache. Lazy-lists based upon arrays are of type
*lazy-list-with-persistence*, even though the underlying array is never
exposed.
*lazy-list-read-point-based* . This lazy-list is based upon a retained
/read-point/ (exposed in the class's slot). I'm sure the /read-point/
has a better name out there - basically, consider it like a /cons/ cell;
but with its /value/ and /next/ changing to valid values only when first
needed (this is where thread-safety would come into play). These are
not always ideal to use - Keeping a head reference to a chain of these
may bloat out the static memory footprint in solutions where a rescan of
the list is not required - this is where a lot of lazy/caching solutions
can get into memory trouble, which is part of why the /read-point/
element is a tool, not the fundamental building block of a lazy-list.
The clichéd self-referencing Fibonacci example really highlights this -
caching is mandatory between the successive elements; but the holder of
the full sequence can not keep a reference to it without a growing
memory footprint (the example in the doc maintains the distinction).
At any rate, this class serves two purposes - to provide the
*lazy-list-with-persistence* cue, and to make /read-point/ available to
the /read-point-built/ function (which can just take it from the class -
the alternative being a new/parallell cached list with associated memory
bloat and redundant traversals).
More information about the heresy-devel
mailing list