The code looks right to me. It creates a delay and then assigns to
this var a *function* that forces the delay whenever it is invoked.
Since delay caches its contents, its behavior matches the factory
style.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
To unsubscribe from this group, send email to clojure+u...@googlegroups.com
For more options, visit this group at http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---
> just started reading it and already have some new perspectives.
> Like, I didn't really know that "let" variables could depend on each
> other. I would have expected that to be let*, but I just looked in
> the API, and I don't see a let*, which seems odd to me. What's that
> all about?
Binding sequentially in "let" and allowing "redefinition" (really
binding of a new local with the same name) later in the vector is
Clojure's behavior and I think it's a good one. One way to think of it
is that let's first argument is a vector of bindings. A vector is a
sequential (as opposed to parallel) collection, so it fits nicely that
the bindings are processed sequentially.
Because parallel bindings are also useful, I think it's an interesting
idea to extend let to allow it to take parallel bindings in a map
instead of a vector. These would act like Common Lisp's "let":
(def a 4)
(let {a 1 b a c 3}
(prn a b c))
1 4 3
(I recall reading about this somewhere, possibly as something Rich
considered and rejected, so it's not an original idea. It's just
something I think about from time to time.)
Note that the arguments to "binding" are processed in parallel and
cannot refer to each other. They are established and released in a
group using a map internally. If the syntax were also a map, it would
be a good cue that they are not handled the same way as in Clojure's
(current) let.
--Steve
Can't you already do this with:
(let [[a b c] [1 a 3]]
(prn a b c))
Can't you already do this with:
(let [[a b c] [1 a 3]]
(prn a b c))
A more fundamental question than how best to do it is - who needs
this? Does someone have a real need for parallel let? Theoretical
arguments about it being clearer (can't have dependencies) aside,
after all most let binding lists are short and certainly local,
where's a compelling use case for more syntax here?
So far it seems like sequential let has proven a useful default, and
I'd rather have one let than two. SML has only sequential let.
Rich
On Wed, Mar 18, 2009 at 10:40 PM, Stephen C. Gilardi <sque...@mac.com> wrote:
> Because parallel bindings are also useful
> > Because parallel bindings are also useful
>
> Could you explain? I don't understand the justification for let in
> Lisp, when let* seems so much more useful.
As I mentioned, I like how Clojure's "let" works. I'm also fully on
board with Rich's suggestion that before figuring out how to do
something, we should first be sure it's important to do. I have not
come up with any compelling reason for Clojure's let to provide more
support for parallel binding than Mark Engleberg pointed out it
provides already.
Since Rich's comment, I've done some reading about this and I saw two
justifications for the behavior of Common Lisp's let:
[a] if one implements let in terms of lambda expressions, parallel
binding produces one lambda expression while sequential binding
produces nested lambdas, one per binding. Parallel binding implemented
this way is simpler and uses less resources. In cases where you don't
require the benefits of let*, use let because it's more efficient.
[b] when reading the expressions bound to names in a let expression
in Common Lisp, you know that each expression stands alone. In
particular you know that it has not been affected by any previous
bindings in the same let.
I recall (perhaps imperfectly) that Rich has commented on [a] in the
past with something like "nobody implements let that way and Clojure's
sequential let is efficient".
Justification [b] sounds like it would be only a minor convenience for
me, but does seem like a decent argument.
Regarding Clojure's "binding" operation, if we were not constrained by
history, I think using a map for its bindings would be an improvement.
The bindings it makes are different in several key ways from those
made by the binding vectors used everywhere else in Clojure:
- the expressions are evaluated sequentially, but the binding is done
in parallel
- binding doesn't introduce new names, rather it must work with vars
that already exist
- the bindings it makes are thread-local and have dynamic scope
rather than lexical scope
- binding doesn't support destructuring
Using a map would serve as a clear marker that these bindings are not
like anything else in Clojure. I think the case for this became
stronger when Clojure changed to require vectors wherever "let-like
bindings" were done (e.g., dotimes) some months ago. "binding" uses a
vector, but its bindings are not "let-like".
--Steve
A more fundamental question than how best to do it is - who needs
this? Does someone have a real need for parallel let?
Rich
I like the sequential let too. For one thing, it allows for the breaking apart of complex expressions into more comprehensible parts, with well named intermediate variables resulting in self documenting code.
> I thought of let as a sort of variable declaration, and so, one
> would want to keep it simple and not do complex calculations in the
> let binding expressions.
>
> On the other hand, the sequential mutually-dependent let bindings
> are of course legal Clojure and completely immutability-safe.
>
> If it is considered idomatic, then that's great, as it safely
> simulates the sequential building up of values usual to procedural
> programs.
Exactly. As I explain in my monad tutorial (http://onclojure.com/
2009/03/05/a-monad-tutorial-for-clojure-programmers-part-1/), let is
just an optimized implementation of the identity monad. As such it
looks like the "natural" way to write multi-step computations in
which each step depends on the results of the preceding ones. In
fact, how would you write such computations otherwise?
Konrad.