[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Chicken-users] Which eggs to migrate from Chicken 3 first?

> > Streams are not lists.  Scheme ensures there are
> > substantial disadvantages to using streams rather than
> > lists, due to the underlying promises that require
> > numerous type conversions, so streams should be used only
> > when the sequence of elements is truly infinite (such as
> > mathematical series) or when there is some clear advantage
> > of laziness (such as reducing the number of passes through
> > a large data set).
> I also find that any data structure in general which is
> built on I/O works well as a stream.  For the current NLP
> app I'm working on, I need to build a parse graph from a
> port.  Slurping up the whole input at once could require too
> much memory, and also would prevent the parser acting as a
> proper (buffered) Unix filter.  On the other hand, since the
> algorithm for determining how much text I need to work with
> is dynamic, I can't just read chunks at a time (the basic
> unit I want to work on is a sentence, but I don't know what
> a sentence is until the parse is finished).  So I build the
> graph as a lazy stream of nodes, and the algorithm
> transparently expands only as much input as needed.
> From what I've seen Alejandro also uses streams primarily
> with I/O - it's a very natural combination.

Just thought I'd mention another example where I do this, which I feel
is relevant to this discussion: http://wiki.freaks-unidos.net/xc, my
command-line calculator.

The whole lexing/parsing is done using streams: the input is received
by the lexer as a stream of characters and turned into a stream of
tokens (by procedure 'lexer'); the parser itself receives this stream
of tokens and returns a stream with the numbers that should be printed
(by procedure 'parse-input').  The whole evaluation of the input is
thus simply (and here I'm quoting verbatim):

    (lambda (obj)
      (if (number? obj)
        (output-num obj)
        (format #t "~S~%" obj)))
    (parse-input (lexer (port->stream))))

Very clean, isn't it?  All is evaluated lazily, allowing things such
as output-num to be redefined during the evaluation, to output numbers
in a different formats.

Given the primitives provided by stream-ext and stream-parser, xc does
not have to add complexity of its own to handle streams but can deal
exclusively with the problem it attacks, of evaluating arithmetic
expressions and such.

And, obviously, the sequence of elements in these streams is finite
and, even if the tricks such as redefining output-num didn't matter,
using streams instead of lists still has the advantage that Alex Shinn
mentions of loading elements from the input lazily.  Perhaps more
importantly, it also produces the output as soon as it is available
(so that one can actually use the calculator interactively and see the
result of an expression immediately after entering it).