-al wrote:
> However, if that is provided, are you wedded
to the idea that lambda's
> feature of semi-variable-arity argument lists like (a b . c) must
also
> be supported?
>
> If you're thinking you want that so that you can use a destructuring
> idiom like:
>
> (let* ((foo '(1 2 3 4 5))
> ((values x y . z) (unlist foo)))
> (list x y (apply + z)))
> => (1 2 12)
>
> I would like to point out that that requires Z to be bound to a list
> of three new pairs and *not* to the cddr of FOO, so this really isn't
> an appropriate idiom for the typical operation of extracting the first
> few elements of a list and the remainder.
Well observed.
> As has been suggested, the
> appropriate construct for that would be a pattern-match facility.
Not necessarily. More deconstructors
for lists of a certain length would cover most
cases already, e.g. unccons, uncccons,
unccccons, ....
Maybe one can even come up with a naming
convention (some sort of Van Dijk
context-free language) encoding more
complicated binary trees into the name
of the deconstructor; similar to c(a|d)^*r
specifying a path from the root in a binary tree.
Pattern matching is of course more flexible,
but also much more expensive.
In fact, my idea concerning the "destructor
style" of programming is that this SRFI
could be a stepping stone towards patterns.
The severe limitation of pattern
matching in most languages is that you
can only decompose concrete
data structures. Often you want to decompose
an abstract data structure, i.e.
I don't want to know how the priority
queue is represented, I just want to extract
the minimal element. Now if you allow
this, and the language has side-effects,
then user-defined decomposition operations
will shred your clean semantics,
and maybe also limit the compiler's
options for making good code. This does
not immediately imply, however, that
it's a bad idea. Scheme is full of procedures
that behave like functions but are implemented
using side-effects.
Now the mechanism in this SRFI with
uncons/unlist/unvector is just an inspiration
for writing application-specific deconstructors.
I would be glad to see someone
take up the challenge of defining a
good pattern matching mechanism that can
apply application-specific deconstructors
in a convenient way.
> If you drop support for semi-variable-arity,
and you drop support for
> zero-value clauses (which I will discuss more below) then you can
dump
> the VALUES keyword entirely, because (values . x) clauses can be more
> simply and clearly written -- without the triple-open-paren -- using
> values->list:
True. But I am not yet convinced that
getting rid of the VALUES keyword is worth
dropping semi-variable arity, or zero-valued
clauses. Three parens is bad, but
is it that bad?
> With respect to zero-valued clauses: I'm not
sure it really makes
> sense to allow "binding clauses" that do not actually create
any
> bindings. In what situations are you thinking these would be
useful?
If you look at imperatively written
algorithms, e.g. often found in numerical analysis,
you see variable bindings and statements
intermixed at a high rate, due to short-lived
temporary variables. This is not specific
for Scheme but for the application domain.
I wrote quite a bit of Ocaml code lately
and I quickly adopted the indentation
convention of not indenting by scope
but by control structure. Example:
let x1 = foo1
x
in foo2
x1;
let x3 = foo3
x1
and x4 = foo4
x1
in foo5
x1 x3 x4;;
If this stuff goes on for 30 lines and
more then a more conventional indentation
makes you want to tilt you monitor 45
degrees clockwise...
The nature of this sort of code is that
there is a linear sequence of things
but the scopes flash in and out like
crazy.
Of course, the usual reflex of programmers
is to forget about the actual
data flow and write one giant LET*.
In Scheme everything has a value and
hence you just write a binding (some-dummy-variable
<no-values>)
when you need to execute a statement.
The only thing the ((values) <expression>)
notation accomplishes is getting rid
of the dummy variable (and the notation for
that is not even very good).
Now what you propose is actually improving
the support for imperative
programs. No doubt, I am in favor of
that. (And it annoyed me more than
once that (begin) is not portable.)
However, I am not sure if this SRFI is
the right place to attempt this. Fiddling
with LET is touchy already.
Instead of modifying BEGIN, I am more
in favor of adding another production:
<binding spec> --> (begin <expression>*),
which means nothing is bound and the
expressions are executed
for side-effects, throwing away whatever
values they produce if any.
Good style, or not, it allows to write
(let* ((y (foo x))
(begin (display y))
(z (bar-trashing-argument!
y)))
z)
Two downsides: a) Looks funny. b) You
cannot bind a variable named 'begin'.
Any other? If this is all, it could
be worth it.
Sebastian.