Request Feedback on Clojure Blog Article

6 views
Skip to first unread message

Keith Bennett

unread,
Mar 16, 2009, 11:44:22 PM3/16/09
to Clojure
All -

I am a relative newcomer to Clojure, but have been really enjoying
learning and using it.

I've published an article on my blog at http://snipurl.com/dyxz7.
It's about some of my impressions of Clojure based on my studies and
porting of a Swing app to Clojure (a previous article discussed a
JRuby port of it). It's kind of intended for newcomers to Clojure from
the more conventional languages.

If any of you could check it for accuracy I would appreciate it. Of
course, feel free to comment on it too.

Thanks,
Keith Bennett

Timothy Pratley

unread,
Mar 17, 2009, 12:44:55 AM3/17/09
to Clojure
Hi Keith,

I don't follow the 'lazy-init' part... It seems to me that you create
a delay but force it immediately which is effectively just running
create-a-text-field. That behavior seems different from the factory
style return if exists or create you originally started with. I don't
see the need for either when you could just (def text-field (doto
(JTextField. 15) (.setToolTipText "Input a temperature.")))) or (def
text-field (create-a-text-field))?

Regarding unnamed parameters, I just wanted to be pedantic and point
out that the following compiles with javac:
class test {
int testfn(int _) {
return 0;
}
}
_ is not treated any differently by the Clojure reader, its just a
useful letter for arguments you don't care about.


Regards,
Tim.



On Mar 17, 2:44 pm, Keith Bennett <keithrbenn...@gmail.com> wrote:
> All -
>
> I am a relative newcomer to Clojure, but have been really enjoying
> learning and using it.
>
> I've published an article on my blog athttp://snipurl.com/dyxz7.

Mark Engelberg

unread,
Mar 17, 2009, 12:50:09 AM3/17/09
to clo...@googlegroups.com
On Mon, Mar 16, 2009 at 9:44 PM, Timothy Pratley
<timothy...@gmail.com> wrote:
>
> Hi Keith,
>
> I don't follow the 'lazy-init' part... It seems to me that you create
> a delay but force it immediately which is effectively just running
> create-a-text-field. That behavior seems different from the factory
> style return if exists or create you originally started with.

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.

Timothy Pratley

unread,
Mar 17, 2009, 2:28:01 AM3/17/09
to Clojure
Quite right, thanks for the explanation!

On Mar 17, 3:50 pm, Mark Engelberg <mark.engelb...@gmail.com> wrote:
> On Mon, Mar 16, 2009 at 9:44 PM, Timothy Pratley
>

Timothy Pratley

unread,
Mar 17, 2009, 9:03:47 AM3/17/09
to Clojure
I have another question! I've found your article very interesting. I
really like how you have buttons and keybindings to the same
AbstractAction. However when I run your app if I press ctrl-x while in
a textbox nothing happens... If I'm not in a textbox it exits. I
believe this is because the textboxes have DocumentListeners which
swallow the ctrl-x. What would be the best way to overcome this
limitation?

Regards,
Tim.


On Mar 17, 2:44 pm, Keith Bennett <keithrbenn...@gmail.com> wrote:
> All -
>
> I am a relative newcomer to Clojure, but have been really enjoying
> learning and using it.
>
> I've published an article on my blog athttp://snipurl.com/dyxz7.

Timothy Pratley

unread,
Mar 17, 2009, 12:11:58 PM3/17/09
to Clojure
I found one way to make 'global hotkeys' (its a bit verbose though):
http://groups.google.com/group/clojure/web/global-hotkey.clj
adapted from http://www.javaspecialists.eu/archive/Issue007.html
Just mentioning it for interest seeing key controls in swing has been
driving me nuts :P

Regards,
Tim.

e

unread,
Mar 19, 2009, 12:28:24 AM3/19/09
to clo...@googlegroups.com
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?

Secondly, also about "let", I remember the thing about C that people critiqued was that you had to define all your variables before you started using them ... only one step improved over languages that had to put them in a special section above their use (sort of sounds like "let"?).  And then along came C++ and java, and folks like me looked like idiots for being old-school and defining stuff at the top of functions instead of delaying until just when you need them.  I eventually bought into that idea: it makes it easy to see whats going on, it makes the names of variables less important, and it delays allocation until everything prior is known to be set up right.  In C++ it's considered a best practice, and I wonder if that community would argue that this is a step in the opposite direction.  So I also wonder if that's why "let" works like it does (like let*)? so that you still think about the order things should be set up?

I can see one argument right now for this c-like approach ... but I'm not sure I buy it -- that in lisp (etc.) the functions typically so short (or should be) that the C++ argument doesn't come into play.  Maybe better, I could see one arguing that you are still supposed to parlay initialization with ever-nested "lets" ... but C doesn't allow nested functions so it's not a valid comparison.  And when all else doesn't seem right you can still use java variables a la refs any time you really want.  Am I on the right track?

Ok, back to reading.  Thanks for writing it.

e

unread,
Mar 19, 2009, 12:38:34 AM3/19/09
to clo...@googlegroups.com
i agree with the comment there that your article is a nice, gentle introduction that makes me go, "cool, now pick another clojure topic.  I'm ready to read it" -- maybe concureny?

Stephen C. Gilardi

unread,
Mar 19, 2009, 1:14:47 AM3/19/09
to clo...@googlegroups.com
--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---


Stephen C. Gilardi

unread,
Mar 19, 2009, 1:40:01 AM3/19/09
to clo...@googlegroups.com

On Mar 19, 2009, at 12:28 AM, e wrote:

> 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

Mark Engelberg

unread,
Mar 19, 2009, 1:53:49 AM3/19/09
to clo...@googlegroups.com
On Wed, Mar 18, 2009 at 10:40 PM, Stephen C. Gilardi <sque...@mac.com> wrote:
> 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

Can't you already do this with:
(let [[a b c] [1 a 3]]
(prn a b c))

Joshua Fox

unread,
Mar 19, 2009, 3:33:14 AM3/19/09
to clo...@googlegroups.com
Great article!
> Although I’ve done only a little Ruby metaprogramming, my sense is that Clojure’s macros make 
> it more powerful than Ruby in this respect
It would be good to see a comparison of metaprogramming with macros. They sometimes are used for similar purposes, but of course are not the same thing.

Joshua

e

unread,
Mar 19, 2009, 7:53:03 AM3/19/09
to clo...@googlegroups.com

Can't you already do this with:
(let [[a b c] [1 a 3]]
 (prn a b c))


if, indeed you can, that seems to violate the very interesting, recent discussion Rich initiated that someone recoined as the Property Design Pattern.  So, on one hand, having let process args lazily is consistent with the spirit of the language, but on the other hand dealing with two arrays where orders have to line up isn't.  Since maps are prefered over pairs of vectors, seems like the map notation is "better"?

Rich Hickey

unread,
Mar 19, 2009, 8:22:08 AM3/19/09
to clo...@googlegroups.com

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

Stuart Sierra

unread,
Mar 19, 2009, 12:21:56 PM3/19/09
to Clojure
On Mar 19, 8:22 am, Rich Hickey <richhic...@gmail.com> wrote:
> So far it seems like sequential let has proven a useful default, and  
> I'd rather have one let than two.

Agreed - sequential let is a better default.

-Stuart Sierra

Joshua Fox

unread,
Mar 19, 2009, 4:42:22 PM3/19/09
to clo...@googlegroups.com
 On Wed, Mar 18, 2009 at 10:40 PM, Stephen C. Gilardi <sque...@mac.com> wrote:
> 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.

Joshua 

Stephen C. Gilardi

unread,
Mar 19, 2009, 6:26:14 PM3/19/09
to clo...@googlegroups.com

On Mar 19, 2009, at 4:42 PM, Joshua Fox wrote:

> > 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

e

unread,
Mar 19, 2009, 9:21:28 PM3/19/09
to clo...@googlegroups.com

A more fundamental question than how best to do it is - who needs
this? Does someone have a real need for parallel let?

Rich


is "parallel let" just a conventional description?  What I mean is that it seems like I could provide an argument c) to that list, namely, that, some day, the assignments really could happen in a concurrent fashion.  It's like how I think "const" was set up as a keyword in java (I think?) just in case they needed it some day.  It also reminds me of how, from what I remember, Dijkstra would make his "if clauses" independent.  That way they could be reordered harmlessly.  By having disjoint conditions, you could also imagine different processes testing each condition in parallel ... knowing only one would succeed, and no ordering specifies any priority/preference.

Keith Bennett

unread,
Mar 19, 2009, 11:15:47 PM3/19/09
to Clojure
Tim -

If you're running Windows or Linux/Unix (anything other than Mac OS
X), then I think the ctrl-x key binding for the shortcut will be
overridden by the text field's ctrl-x key binding, which is to the
'cut to clipboard' action. Try selecting some text and pressing ctrl-
x to see.

Keyboard stuff can be tricky in Swing. This one was my fault
though...I should probably not have used 'x'.

Regards,
Keith

Keith Bennett

unread,
Mar 19, 2009, 11:19:47 PM3/19/09
to Clojure
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. For another, if a calculated value is used more than once, then
using an intermediate variable eliminates the code duplication that
might otherwise be necessary.

- Keith


On Mar 19, 12:21 pm, Stuart Sierra <the.stuart.sie...@gmail.com>
wrote:

Joshua Fox

unread,
Mar 20, 2009, 9:35:49 AM3/20/09
to clo...@googlegroups.com
 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. 

About the 7-part let as shown in your example: Could we get some opinions about whether it is "idiomatic" Clojure?

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.

Joshua

Konrad Hinsen

unread,
Mar 20, 2009, 10:14:41 AM3/20/09
to clo...@googlegroups.com
On Mar 20, 2009, at 14:35, Joshua Fox wrote:

> 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.

Reply all
Reply to author
Forward
0 new messages