Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Road to Clojure Survey

535 views
Skip to first unread message

Kenneth Tilton

unread,
Feb 10, 2009, 2:30:04 AM2/10/09
to
In 25 words or less, why is Clojure better than Common Lisp?

In 100 words or less, why is Clojure better than Common Lisp?

Have you switched to Clojure? If so, from what?


Kaz Kylheku

unread,
Feb 10, 2009, 3:46:54 AM2/10/09
to
On 2009-02-10, Kenneth Tilton <kent...@gmail.com> wrote:
> In 25 words or less, why is Clojure better than Common Lisp?

It has more kinds of parentheses. And symbols have to prove themselves
worthy before they are interned.

> In 100 words or less, why is Clojure better than Common Lisp?

That constraint I can't deal with. I only ``do'' 25 words or less.

And anyway, in this world, by the time your ad execs throw together a monstrous
100 word sound bite, the competition has eaten your lunch.

> Have you switched to Clojure? If so, from what?

NewLisp, mostly. In fact, I switched from NewLisp to Clojure about six times
just today alone! Well, no four. One of those times I went to Scheme first,
then Clojure, and another time I was actually using Common Lisp and /thought/
it was Clojure, until I misspelled some names. The misspellings scrolled off
the screen, and so I didn't know what to unintern to get back to a clean state.
I ended up rebooting the machine.

Pascal J. Bourguignon

unread,
Feb 10, 2009, 5:31:27 AM2/10/09
to
Kaz Kylheku <kkyl...@gmail.com> writes:

> On 2009-02-10, Kenneth Tilton <kent...@gmail.com> wrote:
>> In 25 words or less, why is Clojure better than Common Lisp?
>
> It has more kinds of parentheses. And symbols have to prove themselves
> worthy before they are interned.

This is why it's worse.


>> In 100 words or less, why is Clojure better than Common Lisp?
>
> That constraint I can't deal with. I only ``do'' 25 words or less.

And 25 is not less than 100 anymore?


> And anyway, in this world, by the time your ad execs throw together a monstrous
> 100 word sound bite, the competition has eaten your lunch.


--
__Pascal Bourguignon__

glenn....@gmail.com

unread,
Feb 10, 2009, 6:14:30 AM2/10/09
to

I guess I've switched, from Common Lisp, mostly because I changed jobs
last November and I couldn't take my CL code base with me, so I was
able to examine Clojure from a fresh perspective.

I'm exploring Clojure for my simulation work. The biggest plus for me
in my domain is Clojure's concurrency model: immutable data
structures, agents, STM, and NO USER LOCKS!. This makes it very easy
to spread model calculations across cores without having to protect
against improper concurrent data sharing either with hard to develop/
debug locks or by "convention". Even though Clojure has no built-in
distributed support, it'll eventually get one and until then, it
appears that one can use any of the mechanisms available through the
JVM.

The second big thing for me is the Java interoperability, as my domain
needs access to lots of different math and there's lots of math
libraries for Java that are accessible through Clojure's very nice
Java interop facilities. Additionally, it's very easy to tell Clojure
what the types are of data items (like various CL declarations,
although easier to use) which speed things up. CL's numeric tower is
more featureful, but Clojure's is certainly adequate. There's no
native complex number support, although you can do that in the Java
layer. I haven't yet needed to use complex numbers in my work so
that's not a big deal for me but I have seen some postings of math
types who miss native complex numbers. There's no problem with
Clojure functions being the callbacks for Java code, as Clojure
functions implement all the right Java interfaces to make that work.

The HotSpot JVM seems speedy enough for me and it has a JIT so things
that run a while automatically speed up. For some micro benchmarks in
the areas that I care about, Clojure compares well with SBCL once the
JIT has had a chance to optimize things. The last time I had done any
serious work in Java was in 1997 so I was very pleasantly surprised at
how far JVM performance has progressed.

I initially thought I was going to miss CLOS because my prior code
base heavily depended on CLOS, especially method combination to help
make a lot of boilerplate disappear (in addition to removing the rest
of the boilerplate with a very high-level abstract model definition
language implemented with macros on steroids), but the way maps
(hashmaps), multi-methods, meta-data, and ad-hoc hierarchies work
together mostly make up for the lack of CLOS.

The big step for me here was getting past "it doesn't have CLOS, how
can I model essentially object-oriented simulation models without
something like CLOS?" to "with what's available in Clojure, what's the
Clojure-idiomatic way of creating the model building abstractions I
need?" Once I got past that hang-up, I was able to proceed quite
nicely and am happy with the result. My user-level model building
code will be as nice, concise, and high-level as my CL version was
(this is all work in progress).

The other things I miss in Clojure in addition to the above mentioned
method combination are symbol macros, macrolet, and LOOP (!), but
there are ways around most of these things so their ommision aren't
too painful. For day-to-day programming, LOOP is probably the thing I
miss the most, as any looping has to be done through recursion.
There's a language construct to make tail-recursive calls efficient on
the TCO-less JVM, but I do miss some of the more powerful features of
LOOP (yes, I'm LOOP-evil). However, the Clojure community is very
active so I expect that over time these will be filled in with
libraries.

When I was doing my initial investigation last December, what really
cinched it for me was watching Rich's Clojure for Lisp Programmers
videos and the concurrency video. They are very good and should
answer any questions about whether Clojure is suitable for any
particular task. The concurrency video is particularly good and goes
into technical depth discussing Clojure's concurrency support, the
design decisions Rich faced and why he made certain choices, and an in-
depth code walkthrough of an ant-colony food gathering simulation
using agents. Very cool stuff. Even though the video wasn't
specifically about Java interoperability, he does show building up a
graphical UI to show the ants speeding around in the REPL. Very, very
cool stuff.

My opinion after watching these videos is that Rich has *very* good
design taste. Starting with the goals he wanted, in my opinion, he's
made the right decision every time. It's obvious from the videos that
he has a deep command of programming language theory, which has led
him to choose a lot of semantics and implementation techniques that
fit together well, instead of that "groping in the dark" feeling you
get from say, certain languages.

In summary, I'm not going to say that Clojure is better or worse than
Common Lisp. For my particular domain, I can do some things easier
than I can do in the Common Lisp implementations I have available to
me (an important qualifier!) so it's useful for me. Your mileage may
and will vary.

Oh, I should add that I've purchased the beta of Stuart Halloway's
"Programming Clojure" book from Pragmatic Programmer's and it covers
the language nicely. I was able to breeze through the book in several
hours and came away with a good command of the language. Keep in mind
that it's more geared towards a non-Lisp reader than a CL expert.

Glenn Ehrlich

Kenneth Tilton

unread,
Feb 10, 2009, 9:55:52 AM2/10/09
to
glenn....@gmail.com wrote:
> On Feb 10, 12:30 am, Kenneth Tilton <kentil...@gmail.com> wrote:
>> In 25 words or less, why is Clojure better than Common Lisp?
>>
>> In 100 words or less, why is Clojure better than Common Lisp?
>>
>> Have you switched to Clojure? If so, from what?
>
> I guess I've switched, from Common Lisp, mostly because I changed jobs
> last November and I couldn't take my CL code base with me, so I was
> able to examine Clojure from a fresh perspective.

<snip great response>

Thx! You gave me ideas for a lot of questions. New survey soon (all of
which you have already answered).


kt

Kenneth Tilton

unread,
Feb 10, 2009, 10:10:13 AM2/10/09
to
What is your Clojure Status?
- What is Clojure?
- Never looked at it.
- Looked but did not like because ___
- Will use for some work because ___ but not all because ___ but
continue to use ___ for ___ because ____.
- Will now use instead of ___ because ____
- Other ____


(I think the rest are for anyone who will be using Clojure)

If Java access was a big factor, why not ABCL or AllegroCL?

What do you miss from Common Lisp (or whatever the "from" language was).


Other _______

Raffael Cavallaro

unread,
Feb 10, 2009, 10:54:58 AM2/10/09
to
On 2009-02-10 05:31:27 -0500, p...@informatimago.com (Pascal J.
Bourguignon) said:

> Kaz Kylheku <kkyl...@gmail.com> writes:
>
>> On 2009-02-10, Kenneth Tilton <kent...@gmail.com> wrote:
>>> In 25 words or less, why is Clojure better than Common Lisp?
>>
>> It has more kinds of parentheses. And symbols have to prove themselves
>> worthy before they are interned.
>
> This is why it's worse.
>
>
>>> In 100 words or less, why is Clojure better than Common Lisp?
>>
>> That constraint I can't deal with. I only ``do'' 25 words or less.
>
> And 25 is not less than 100 anymore?

<http://en.wikipedia.org/wiki/Sarcasm>

--
Raffael Cavallaro, Ph.D.

Raffael Cavallaro

unread,
Feb 10, 2009, 11:16:27 AM2/10/09
to
On 2009-02-10 10:10:13 -0500, Kenneth Tilton <kent...@gmail.com> said:

> What is your Clojure Status?

is clojure about to vault into the tilton pantheon?!

> - What is Clojure?
> - Never looked at it.
> - Looked but did not like because ___
> - Will use for some work because ___ but not all because ___ but
> continue to use ___ for ___ because ____.

will use for some work but not all because of:

cross platform java libs, especially cross platform gui libs

integration with emacs/slime

active enthusiastic user community and much contributed code

mikel evins likes it!

built in ease of threading due to immutablility of clojure data
structures (i.e., pure functional semantics).

possiblility of easy web deployment via applets (though not currently
high on my list)


continue to use CCL because of excellent mac os x integration.


> - Will now use instead of ___ because ____
> - Other ____
>
>
> (I think the rest are for anyone who will be using Clojure)
>
> If Java access was a big factor, why not ABCL or AllegroCL?
>
> What do you miss from Common Lisp (or whatever the "from" language was).

maturity of the language. code from six months ago often doesn't work
because breaking changes are still being made to the language.

>
>
> Other _______


--
Raffael Cavallaro, Ph.D.

Kenneth Tilton

unread,
Feb 10, 2009, 12:02:38 PM2/10/09
to
Raffael Cavallaro wrote:
> On 2009-02-10 10:10:13 -0500, Kenneth Tilton <kent...@gmail.com> said:
>
>> What is your Clojure Status?
>
> is clojure about to vault into the tilton pantheon?!

No, just curious. Thx for the survey response. Mikel is indeed a big plus.

kt

Dimiter "malkia" Stanev

unread,
Feb 10, 2009, 1:49:20 PM2/10/09
to

I'm syncing to the daily builds of clojure, and doing some minor
benchmarks. I'm trying hard to reach the speed of Common Lisp or Java
(I'm running Clojure with the -server option).

Overall, the speed ain't bad, it's about 10x faster than perl, python,
ruby, but x10-x20 slower than most of the Common Lisp Compilers (SBCL,
CMUCL, LispWorks, ACL).

I'm using Lisp for heavy calculations, so I can't really use it, until I
learn to optimize it better.

My base benchmark was the pnpoly.lisp code (point-in-poly).

Other things that I'm missing from Common Lisp are some form of
"apropos" and some way of disassembling the functions - I'm doing this
regurarly with Common Lisp in order to learn the peculiarities of the
various implementations, and gather knowledge of best practices myself
(off course there are papers by Fateman, and others about single and
double floating points).

Another strange peculiarity is the tagging - sometimes you have to do
(#^Float number), sometimes (number (float 10.0)) - it seems not
consistent, but granted this is not where the language is shining.

I think it's not the language right now for my needs, but my become, as
soon as I start explore web development more and more.

The JAR file is rather small, compared to the ABCL (not fair comparison,
I know different things - but still to give you perspective).

It definitely looks useful, and can teach you how to write even more
functional code (still trying to prevent myself from imperative thinking).

Alex Mizrahi

unread,
Feb 10, 2009, 5:15:45 PM2/10/09
to
ge> I'm exploring Clojure for my simulation work. The biggest plus for me
ge> in my domain is Clojure's concurrency model: immutable data
ge> structures, agents, STM, and NO USER LOCKS!.
ge> This makes it very easy to spread model calculations across cores
ge> without having to protect

spreading across cores makes sense if it speed ups things, right?
even if there are no apparent locks, STM etc., etc. might have its
overhead. and it might be possible that Clojure running on all 8 cores
works actually slower than SBCL running on single one.

so, all these features are sort of "cool", but totally meaningless
without proper benchmarking data. (if we're speaking about CPU-bound
applications.)


Alex Mizrahi

unread,
Feb 10, 2009, 5:17:50 PM2/10/09
to
KT> - Looked but did not like because ___

i did not like it because it is not a Common Lisp!

plus, at time i was looking at it, it threw errors in Java backtrace format,
that is, you've misplaced paren, and it throws ton of bullshit at you.
very unpleasant. (it seems they've aldready fixed this)


André Thieme

unread,
Feb 10, 2009, 7:41:31 PM2/10/09
to
Dimiter "malkia" Stanev schrieb:

> Kenneth Tilton wrote:
>> In 25 words or less, why is Clojure better than Common Lisp?
>>
>> In 100 words or less, why is Clojure better than Common Lisp?
>>
>> Have you switched to Clojure? If so, from what?
>>
>>
>
> I'm syncing to the daily builds of clojure, and doing some minor
> benchmarks. I'm trying hard to reach the speed of Common Lisp or Java
> (I'm running Clojure with the -server option).
>
> Overall, the speed ain't bad, it's about 10x faster than perl, python,
> ruby, but x10-x20 slower than most of the Common Lisp Compilers (SBCL,
> CMUCL, LispWorks, ACL).
>
> I'm using Lisp for heavy calculations, so I can't really use it, until I
> learn to optimize it better.

In general one can suggest to give type hints to speed up things.
For a little test function that Rainer Joswig gave here in c.l.l some
weeks ago I got from x15 more runtime than sbcl to half the runtime of
sbcl with a few type hints.
But sometimes those won’t help. Currently the bignum lib from Java seems
to be pretty slow.
For Java 7 some drastic improvements are visible on the horizon:
http://markmail.org/message/7conncsespvrlazn

Another general suggestion is to run things in parallel.
This makes of course only sense if your algorithms are parallelizable.

If they are not, and if you are using Clojure datastructures (such as
sets, vectors, hashmaps, ...) instead of Javas it can also be in some
cases a disadvantage. The Clojure versions simply have to do more work
under the hood. These datastructures are not only threadsafe, but also
concurrency ready, which is even more expensive.

> Other things that I'm missing from Common Lisp are some form of
> "apropos"

So find-doc did not give you satisfying results?
It can also take a regex...


> and some way of disassembling the functions

There are many Java tools that can support you.
Did you try jad to decompile .class files into .java files?
(in that sense Java is the Assembler for the JVM :))
But you can also use bytecode viewer.


> Another strange peculiarity is the tagging - sometimes you have to do
> (#^Float number), sometimes (number (float 10.0)) - it seems not
> consistent, but granted this is not where the language is shining.

As you can see, float is just an ordinary function, but does implicitly
give a type hint:
http://code.google.com/p/clojure/source/browse/trunk/src/clj/clojure/core.clj#1813

And btw, (number (float 10.0)) produces an error, as the function number
does not exist. Did you mean number? maybe?
Type hints can be given very consistent with the reader macro #^
as in #^TYPE


> I think it's not the language right now for my needs, but my become, as
> soon as I start explore web development more and more.

I hope you did not base this on the one result of your poly test case.
Although you know of course much better your needs. If that benchmark is
what you are basically doing all the time, then it’s very understandable.


André
--
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org/

André Thieme

unread,
Feb 10, 2009, 8:25:59 PM2/10/09
to
Pascal J. Bourguignon schrieb:

> Kaz Kylheku <kkyl...@gmail.com> writes:
>
>> On 2009-02-10, Kenneth Tilton <kent...@gmail.com> wrote:
>>> In 25 words or less, why is Clojure better than Common Lisp?
>> It has more kinds of parentheses. And symbols have to prove themselves
>> worthy before they are interned.
>
> This is why it's worse.

The question at this point could be why this is worse.
Honestly, I see only advantages here.
The [] and {} give some hold to the eye, which I find nice.
One can of course argue that it is less consistent, and that a „real
Lisper” will only want to use (). But let’s not forget that () them-
self are reader macros. And if we want to argue with consistency in
mind, then those people should not make use of other reader macros in
CL as well, such as ' #' ` , ,@
I however see that a few handselected reader macros are a great thing.
They should exist for the most commonly used tasks.
Lists are mostly used in Clojure to write code :)
Vectors have taken more or less the place of the list.
One can „cons” elements to it (the function really is conj) and traverse
it with the same performance as lists. But random access is done in near
constant time. Also reversing a vector is much faster, though this is
less often needed, as conj adds elements at the end of the vector, not
to the front (as with lists).

Passing around small hashmaps is so simple.
(some-function {:a 10, :b 20}) vs

(let ((temp (make-hash-table)))
(setf (gethash :a temp) 10
(gethash :b temp) 20)
(some-function temp))


And the other thing that Kaz said about interned symbols also makes sense.
For example Hans Hübner writes in his blog:
„Clojure wants to be a Lisp, but it explicitly does not try to be
backwards compatible. This opened a rather large design space to Rich
Hickey, and some of the choices he made really do make sense. He
specifies a reader, yet his reader does not intern symbols. That is a
big win, as it allows the reader to actually work with arbitary Clojure
source files. In Common Lisp, one needs to re-implement a full reader
which does not intern symbols if one wants to read Common Lisp source
files. This is kind of ironic, as the "Code is Data" mantra that we keep
repeating does not really reflect what is possible in practice.”
Soure: http://netzhansa.blogspot.com/2008/10/trying-clojure.html

Dimiter "malkia" Stanev

unread,
Feb 10, 2009, 8:48:59 PM2/10/09
to
[snipped a lot of useful information - keeping it for later to read]

> I hope you did not base this on the one result of your poly test case.
> Although you know of course much better your needs. If that benchmark is
> what you are basically doing all the time, then it’s very understandable.
>
>
> André

Thanks Andre!

No I'm not going away from it - rather I'm staying - i like the
interactivity of it, the funny thing about it running on JVM (for good
or bad, I can get this thing to run on almost any platform, because it
uses JVM).

Also Rich Hickey seems to know his stuff, and only following his
creation, is enough to grasp powerful ideas, even if I may not use his
language at the end professionally.

Definitely I'm staying, and I'm going to dabble into it even more...

For example ARC did not leave me with that impression - to me arc
sounded like Common Lisp in Rap Lyrics/Slogan... And fact is Paul Graham
is still my favourite lisp person (I've read his stuff). Maybe I'm
missing something from ARC too - but that's another topic.


Pascal J. Bourguignon

unread,
Feb 10, 2009, 10:06:28 PM2/10/09
to
André Thieme <address.good.un...@justmail.de> writes:

> Pascal J. Bourguignon schrieb:
>> Kaz Kylheku <kkyl...@gmail.com> writes:
>>
>>> On 2009-02-10, Kenneth Tilton <kent...@gmail.com> wrote:
>>>> In 25 words or less, why is Clojure better than Common Lisp?
>>> It has more kinds of parentheses. And symbols have to prove themselves
>>> worthy before they are interned.
>> This is why it's worse.
>
> The question at this point could be why this is worse.
> Honestly, I see only advantages here.
> The [] and {} give some hold to the eye, which I find nice.
> One can of course argue that it is less consistent, and that a „real
> Lisper” will only want to use (). But let’s not forget that () them-
> self are reader macros. And if we want to argue with consistency in
> mind, then those people should not make use of other reader macros in
> CL as well, such as ' #' ` , ,@

Let's say that it's a personal taste. (And indeed, I use (function x)
rather than #'x, and sometimes (eg for pedagogical reasons), I use
(quote x) instead of 'x.

Reducing the set of special characters used to write code let you
write it faster. Keying in English is fast, because you only use
letters, and a very small number of punctuation, compared to keying in
popular programming languages. If you resist the temptation of using
a lot of reader macros in lisp, then you can type lisp code as fast as
English text.


Also, it would be nice if the language didn't use all the characters,
and left some for user's reader macros. In Common Lisp, {}, [] , !?
and a few others are reserved to the user. (Of course, with unicode
this is less of a problem).

> Passing around small hashmaps is so simple.
> (some-function {:a 10, :b 20}) vs
>
> (let ((temp (make-hash-table)))
> (setf (gethash :a temp) 10
> (gethash :b temp) 20)
> (some-function temp))

This is crazy. Nobody write such a form!

(htable :a 10 :b 20)
--> #S(HASH-TABLE :TEST EXT:FASTHASH-EQL (:B . 20) (:A . 10))


>
>
> And the other thing that Kaz said about interned symbols also makes sense.
> For example Hans Hübner writes in his blog:
> „Clojure wants to be a Lisp, but it explicitly does not try to be
> backwards compatible. This opened a rather large design space to Rich
> Hickey, and some of the choices he made really do make sense. He
> specifies a reader, yet his reader does not intern symbols. That is a
> big win, as it allows the reader to actually work with arbitary Clojure
> source files. In Common Lisp, one needs to re-implement a full reader
> which does not intern symbols if one wants to read Common Lisp source
> files. This is kind of ironic, as the "Code is Data" mantra that we keep
> repeating does not really reflect what is possible in practice.”
> Soure: http://netzhansa.blogspot.com/2008/10/trying-clojure.html

We only need to add a CDR for a READTABLE-PARSE-TOKEN hook to be able
to read uninterned sources without reimplementing the reader.

But even with the current standard, the point is that reader macros
are not to be abused for lisp programs. It's better in lisp programs
or embeded DSL to use normal functions or macros such as (htable :a 10
:b 20) than to use a reader macro such as {:a 10 :b 20}.

Reader macros are designed to build user level DSL.

If you need to read a DSL source without "interning" it, then you
should use a normal parser.

--
__Pascal Bourguignon__

daniel....@excite.com

unread,
Feb 11, 2009, 10:48:56 AM2/11/09
to
On Feb 10, 10:06 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:

> Reducing the set of special characters used to write code let you
> write it faster.  Keying in English is fast, because you only use
> letters, and a very small number of punctuation, compared to keying in
> popular programming languages.

I beg to point out that some folks can touch-type the non-letter
keys...
and although clearly farther away from home position and therefore
slower,
each non-letter used in a non-letter-rich syntax will generally
represent
many letters of lisp symbol. non-letter-rich syntax can be an
effective
compression for 1) reading, 2) thinking, and yes also 3) typing, if
one
touch-types the non-letters.

That said, the whole reason I like and use the CL type of Lisp syntax
is
because my preferences agrees with yours on:

Raffael Cavallaro

unread,
Feb 11, 2009, 11:09:16 AM2/11/09
to
On 2009-02-10 22:06:28 -0500, p...@informatimago.com (Pascal J.
Bourguignon) said:

> Also, it would be nice if the language didn't use all the characters,
> and left some for user's reader macros. In Common Lisp, {}, [] , !?
> and a few others are reserved to the user. (Of course, with unicode
> this is less of a problem).

This is not an issue for clojure:
from: <http://clojure.org/reader>
"The read table is currently not accessible to user programs."

Of course many common lisp users will think this more a bug than a feature...
--
Raffael Cavallaro, Ph.D.

Message has been deleted

TomSW

unread,
Feb 11, 2009, 12:21:36 PM2/11/09
to
On Feb 10, 4:10 pm, Kenneth Tilton <kentil...@gmail.com> wrote:
> What is your Clojure Status?

I'm using it to learn the Java libraries without having to use Java. I
don't think I'll be lucky enough to find another Lisp job soon:
finding IT jobs in Belgium with no Dutch is hard enough. So I'm
preparing for the worst...

> If Java access was a big factor, why not ABCL or AllegroCL?

Clojure in non-proprietary and has lots of interesting features built-
in, in particular, concurrency, abstract datatypes (collections,
mappings), lazy evaluation.

> What do you miss from Common Lisp (or whatever the "from" language was).

I haven't been using Clojure for long enough to miss much of Common
Lisp.

...

Another interesting approach to leveraging Java libraries is Jnil
(http://common-lisp.net/project/jnil/), which actually translates them
into Common Lisp. The maintainer could really do with some support.

cheers,
Tom

André Thieme

unread,
Feb 12, 2009, 7:15:48 PM2/12/09
to
Kenneth Tilton schrieb:

> In 25 words or less, why is Clojure better than Common Lisp?
> In 100 words or less, why is Clojure better than Common Lisp?

The word „better” is nothing objective, so people can disagree on what I
personally rate as better.
My list of things that I like in Clojure better than CL
(in no particular order):

1. It basically supports one major paradigm/style of programming,
the functional one. CL too does support it, but CL also supports
other styles, and it is even very idiomatic to do imperative
programming in CL.
People will disagree if offering a smaller number of programming
style is better or not, but I like it better. It means that most
developers will come up with more comparable/compatible solutions.
Whatever one may think about the Java programming language, it’s a
fact that it offers a very large set of libraries. This is due to
the limits which are built in into the language. There is basically
one way (few ways) to do it, instead of many ways, as in CL.
So, I prefer to have one major style in Clojure. It happens to be
the one I preffered since many years in CL too, the functional one.

2. Concurrency ready. I see basically 3 or 4 languages out of the
many hundreds in use, that are seriously capable of doing concurrent
programming: Clojure, Erlang, Haskell, and also going there, F#.
While it is not impossible to do it in other languages, Clojure does
make it really easy. It’s in some sense the argument that was in
favour of CL in the past 15-20 years: „Yeah, mathematically speaking
it is possible in other languages too. In principle one can solve any
problem in Assembler. It’s just that it is much easier in Lisp, as it
comes with powerful abstractions”.
Clojure has gone the evolutionary step into the right direction (in
my opinion), by providing standard tools for concurrency. Other langs
like CL or Python can also do functional programming. But the problem
is that they also offer an alternative way. As long that exists the
devs have to program by agreement, and they need to be very strict.
Currently not many languages have a good implementation of a STM with
Multiversion Concurrency Control. I think for F# one is currently
being developed. But this really should come with the lang itself,
as this would be an integral part. If this is an addon lib, then
other implementations may arise, which can (no doubt) also have ad-
vantages, but brings back incompatibility.

3. Modernized syntax. It makes totally sense to me (you might disagree
of course) to have some basic syntax support for the most common
operations. The CL committee had the same idea in mind and intro-
duced ' #' #+ #- ` , ,@ which, again, makes very much sense
to me. To make access to datastructures other than lists easier,
Clojure went a step further, and provides reader macros for vectors
(which can replace lists to a great part), hashmaps and sets, and
of course strings (and regular expressions).
I now hate it so much when I go back to CL that everything is so
chatty. It does not buy me anything that I have to bloat up my code
for typical tasks.
Clojures datastructures are fully integrated into the macro system.

4. Lazyness. In many places Clojure is very lazy :)
This is great. Not only does that improve performance greatly, no, it
also allows new idioms and a more elegant programming style. It’s
well integrated.

5. It runs on the JVM. Today many languages do run in a VM (like CL,
Python, C#, ...). Why develop your own when you can have the most
mature one?
Chosing the JVM brings also tons of advantages:

a) Operating system does not matter so much anymore. CLs are
available for most plattforms too, though this does not always
help. No recompilation needed. I can switch back between the OSes
and continue coding. The devs of the same team have the choice of
their OS. The employer would not have to force us anymore to use
something we don’t like. The plattform in the end is always the
JVM.

b) Giant lib. Want a server for RESTful applications? Integration
with SAP? The JVM can not do magic, but it really haves very
much to offer, library wise.

c) Will help to create much more Lisp jobs.
I personally left two times my home city to get a Lisp job. One
time I even left my country for it. As Java is the most popular
programming language it is more likely to find interesting pro-
jects in cities of my choice. Of course I don’t want to do Java
programming, but there will always be Java companies (probably
the not too big ones) that will accept you in their team as a
Clojure developer. True, it potentially can bring in some diffi-
culties if you are the only one who can read/write Clojure code.
But some companies will weight that against the 2x - 20x boost in
productivity that Lisp can bring over Java. Clojure brings me into
the position that I suddenly can negotiate something with the
company.


So, maybe a bit more than 100 words, but here you go.
The points that I listed will not be the ones that everyone else will
also find „better”. I however do.


> Have you switched to Clojure? If so, from what?

Yes, switched from CL to Clojure.
And if it is not necessary, I would not want to go back.
But sure, if Clojure is no option then CL would always be my next
choice.

André Thieme

unread,
Feb 12, 2009, 7:23:41 PM2/12/09
to
Francogrex schrieb:

> On 10 feb, 16:10, Kenneth Tilton <kentil...@gmail.com> wrote:
>> What is your Clojure Status?
>> - What is Clojure?
>> - Never looked at it.
>> - Looked but did not like because ___
>> - Will use for some work because ___ but not all because ___ but
>> continue to use ___ for ___ because ____.
>> - Will now use instead of ___ because ____
>> - Other ____
>
> I have looked at clojure but don't want to use it because simply it's
> not "common lisp" and it has that "unhealthy" connection to Java
> (either directly defined or not). In addition I am against the
> dispersal/waste of efforts that sprout different programming languages
> instead of concentrating it all to improve the already existing ones.
> I have more respect for an old language like lisp that has matured and
> evolved over the time (albeit to different dialects- of course I would
> have preferred that they stay united-). Now unless you think of
> clojure as an evolution and an extension of common lisp...

I respect your opinion, however, I would like to mention two things:
1) The connection to Java can be seen by some folks as very healthy.
2) When you argue about matureness and evolution, then Clojure could
be interesting. The language is much more simple than Lisp. And
some core parts of a Lisp were already provided by the JVM. For
example the GC, the Exception system and some tenthousand ready to
use methods. In that regard, Clojure can be seen as even more mature
than CL implementations. As Java is the most popular programming
language it gets on a daily basis more man hours of testing than
all CLs got in the past years. Billions were invested by the biggest
IT companies into the JVM, and it went through an evolutionary
process in which it got very mature.

Speaking of evolution: Clojure learned from Lisp. So it is not a
new language that started from scratch. No, it inherited the DNA
of its parents. It took the efforts of research in the past 50
years and continued to evolve from that point. So, it is the next
step in evolution of Lisp.

André Thieme

unread,
Feb 12, 2009, 7:30:09 PM2/12/09
to
Kenneth Tilton schrieb:

> What is your Clojure Status?
> - What is Clojure?
> - Never looked at it.
> - Looked but did not like because ___
> - Will use for some work because ___ but not all because ___ but
> continue to use ___ for ___ because ____.
> - Will now use instead of ___ because ____
> - Other ____

It is: I use it daily for professional work, but also nearly every day
for personal programming fun.
I use Clojure now instead of CL mostly, and will port my code.


> (I think the rest are for anyone who will be using Clojure)
>
> If Java access was a big factor, why not ABCL or AllegroCL?

Because those are CL implementations which come with clos.
I like clos more than the object system of Java, but this is just my
opinion. A fact however is, that those object models are not compatible.
It is very ugly to work with Java from within CL.
So, if access to Java would be a big factor, then one should definitly
use a language which was designed with that idea in mind.


> What do you miss from Common Lisp (or whatever the "from" language was).

From CL I miss in principle nothing.
I am glad that Clojure has no object system, but instead goes the path
of functional programming.
What I miss though are features of specific CL implementations.
For example SBCLs type inference engine.

Raffael Cavallaro

unread,
Feb 12, 2009, 7:39:41 PM2/12/09
to
On 2009-02-12 19:23:41 -0500, André Thieme
<address.good.un...@justmail.de> said:

> And
> some core parts of a Lisp were already provided by the JVM. For
> example the GC, the Exception system and some tenthousand ready to
> use methods. In that regard, Clojure can be seen as even more mature
> than CL implementations.

Not really. This argues for the maturity of the *platform* on which
clojure is built (i.e., the jvm). Clojure itself is clearly still not
mature. There's a thread still running on how to fix mod:

user=> (mod -3 3)
3

So clojure, the language, not the platform it is built on, is still not
nearly as mature as common lisp.
--
Raffael Cavallaro, Ph.D.

Alex Mizrahi

unread,
Feb 13, 2009, 7:12:48 AM2/13/09
to
AT> 1) The connection to Java can be seen by some folks as very healthy.

oh yes, it is very healthy (for a functional language!) to have "recur"
special
operator instead of normal tail recursion because of JVM deficiency.

AT> In that regard, Clojure can be seen as even more mature
AT> than CL implementations.

platform maturity has nothing to do with language implementation maturity.
i have here a build labeled "2008-09-16", just few months ago error handling
was totally broken and weird as it was spitting java backtraces:

user=> (())(()))
java.lang.ClassCastException: clojure.lang.PersistentList$EmptyList cannot
be cast to clojure.lang.IFn
java.lang.ClassCastException: clojure.lang.PersistentList$EmptyList cannot
be cast to clojure.lang.IFn
at user.eval__2290.invoke(Unknown Source)
at clojure.lang.Compiler.eval(Compiler.java:3891)
at clojure.lang.Repl.main(Repl.java:75)

when considerable portions of implementation get reorganized in short
periods of time,
that is *not* called "mature"


Scott Burson

unread,
Feb 13, 2009, 2:07:18 PM2/13/09
to
On Feb 10, 7:10 am, Kenneth Tilton <kentil...@gmail.com> wrote:
> What is your Clojure Status?

I've looked at it, but I have a bunch of code in CL that I'm working
on and see no compelling reason to switch.

If I should decide I need my app to run on the JVM at some point --
which is possible -- I would consider Clojure, but I would also
consider Scala.

-- Scott

André Thieme

unread,
Feb 13, 2009, 2:41:14 PM2/13/09
to
Alex Mizrahi schrieb:

> AT> 1) The connection to Java can be seen by some folks as very healthy.
>
> oh yes, it is very healthy (for a functional language!) to have "recur"
> special operator instead of normal tail recursion because of JVM deficiency.

It’s true, it’s healthy to have recur.
I agree that it is not nice that the JVM does not have tail recursion
yet. But in practice that is not a real issue, as recur is available.

Let’s look at this unreadable mess in CL:

(DEFUN Y (F)
( (LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G G)) H)))
#'(LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G G)) H)))))

(FUNCALL (Y #'(LAMBDA (FN)
#'(LAMBDA (X)
(IF (ZEROP X) 0 (+ X (FUNCALL FN (- X 1)))))))
200)
==> 20100

we can do this in Clojure:
((fn [n] (loop [x n, res 0]
(if (zero? x) res (recur (dec x) (+ x res)))))
12345678)

No Y-Combinator needed, much shorter, much more pleasant for the eye.
Plus: this call works.
When I gave the argument of 2000 in clisp on Windows it crashed.
Stack overflow I guess.


Or another example:
(defun fibonacci (n)
(labels ((fibo-helper (x result)
(if (zerop x)
result
(fibo-helper (1- x) (* x result)))))
(fibo-helper n 1)))

would be in Clojure (without recur):
(defn fibonacci [n]
((fn fibo-helper [x result]
(if (zero? x)
result
(fibo-helper (dec x) (* x result))))
n 1))

The labels is not needed, as fn can be given a name (fn in Clojure is
CLs LAMBDA). Anyway, this is not stack-safe right now, as the JVM does
not support tail call optimazation (though Jon mentioned that the team
of the OpenJDK is working on that).

Now in Clojure we simply replace the call of fibo-helper with recur:
(defn fibonacci [n]
((fn fibo-helper [x result]
(if (zero? x)
result
(recur (dec x) (* x result))))
n 1))

That’s the only change. It is still following the idiom that is needed
in all programming languages for tail recursive calls.
This would also allow us to eliminate the function name we gave to fn.
This makes it again shorter. And if we want to, we can put in a loop:
(defn fibonacci [n]
(loop [x n result 1]
(if (zero? x)
result
(recur (dec x) (* x result)))))

And although I am doing CL since 6 years and Clojure just 4 months or
so, this looks cleaner in my opinion than the CL version.


> AT> In that regard, Clojure can be seen as even more mature
> AT> than CL implementations.
>
> platform maturity has nothing to do with language implementation maturity.
> i have here a build labeled "2008-09-16", just few months ago error handling
> was totally broken and weird as it was spitting java backtraces:
>
> user=> (())(()))
> java.lang.ClassCastException: clojure.lang.PersistentList$EmptyList cannot
> be cast to clojure.lang.IFn
> java.lang.ClassCastException: clojure.lang.PersistentList$EmptyList cannot
> be cast to clojure.lang.IFn
> at user.eval__2290.invoke(Unknown Source)
> at clojure.lang.Compiler.eval(Compiler.java:3891)
> at clojure.lang.Repl.main(Repl.java:75)
>
> when considerable portions of implementation get reorganized in short
> periods of time,
> that is *not* called "mature"

Yes, I understand your position. And also thanks to Raffael, he is also
right.
In fact, Clojure did not reach 1.0 status, so yes, breaking changes can
still occur. But most of the time Rich adds more (very useful)
abstractions and tools to the language, instead of making breaking changes.

However, I also like to look at this from a more practical side.
Clojure is usable now. One can step with debuggers through the code
line by line, one can profile it, compile it into doubleclickable
.jar files (like .exe under Windows) or simply work in Emacs+Slime.
Nearly all parts of the language itself work perfectly. But when
writing real world applications one constantly can be in need of
libs, that already do parts of what you want to do.
Lisps like Allegro, Lispworks or sbcl are being worked on every
day. It’s similar to what happens to Clojure.
It is a bit unfair to make the comparison of what changes in the
core language. CLs is already defined and can not be changed.
Clojure is still open, and Rich can decide to add more to it.
So of course, this will result in more changes in the core when
compared to what happens to CLs.
I worked professionally with Allegro and also Lispworks.
In both I or my workmates discovered bugs in their libs, like
database stuff.
This is much more unlikely to happen when using Clojure, as it
simply offers some ten thousand more functions which still need
to be implemented in CL first, and which were tested some orders
of magnitude more intense than their CL equivalents.
And one could argue if it can be seen as a bug as well, if some
libs are simply not available (like a server for RESTful apps)
in CL. Although Jon writes a lot of provocative material here,
and sometimes explicitly enjoys to bring in some trolling, he
also mentioned very true things, such as that some CL libs have
like 80 users. Not 80k, but a mere 80. Development on important
stuff does not happen as much as I wish (are cursors already
available in sql libs?).
Clojure is actively and intensly used by some hundred people.
We test every new feature, and honestly, I did not stumble upon
lot’s of bugs. Rich fixes them so fast, it’s really nice.

So, if I see the whole variety of things that are available when
programming in Clojure, then only a tiny fraction of it is not
mature.
It’s true that there are some more bugs, but also check out what
SBCL changes:
http://www.sbcl.org/all-news.html

Just scroll down this list. SBCL exists since years, and not one
main developer is working on it, but 5-25.
When I look at this list I see not interesting additions to the
main language and libraries. Tons of bug fixes for core stuff.

Maybe it’s okay to call SBCL not mature, and only see the main
two commercial CLs (Allegro and Lispworks) this way.
I don’t know what they are doing, but they still do bugfixes
constantly. And although they had 15+ more years of time than
Clojure, they still need to fix core stuff. And in their libs
they have also several bugs that my companies uncorvered.

As much I understand the POV that Clojure is not mature, because
of the good reasons you and Raffaello gave, I see it differently
and would call it already mature.
It already offers right now basically everything that is needed
for professional and commercial development. What mostly happens
is that even better abstractions are added every few days.
For me it’s extremly difficult to go back doing CL, because I miss
so much. Kenny asked in this thread also what I miss from CL.
I have to think long to come up with maybe a few points.
But the other direction.. that is the real problem.

Raffael Cavallaro

unread,
Feb 13, 2009, 7:49:42 PM2/13/09
to
On 2009-02-13 14:41:14 -0500, André Thieme
<address.good.un...@justmail.de> said:

> we can do this in Clojure:
> ((fn [n] (loop [x n, res 0]
> (if (zero? x) res (recur (dec x) (+ x res)))))
> 12345678)

we can do this in common lisp:

((lambda (n) (loop for x from n downto 0 summing x)) 12345678)

and when we do it in Clozure Common Lisp (dx86cl64) it runs 10 times as
fast as your clojure code on the same machine.
--
Raffael Cavallaro, Ph.D.

André Thieme

unread,
Feb 13, 2009, 9:46:26 PM2/13/09
to
Raffael Cavallaro schrieb:

We were talking about recur and its advantages. The issue was not about
performance.

It’s likely that Clozure CL or also SBCL will run much faster, as
Clojure would use boxed numbers, which have to be unboxed first and then
boxed again.
One would have to give type hints, as in:

(loop [r (long 0) n (long 12345678)] (if (zero? n) r (recur (+ r n) (dec
n))))

Now that runs probably at very comparable speed.

Anyway, if I want to outperform your Clozure Cl code above by a factor
of 150 in Clojure, then this would do:
(defn sum [n] (+ (/ n 2) (/ (* n n) 2)))

Raffael Cavallaro

unread,
Feb 13, 2009, 11:06:45 PM2/13/09
to
On 2009-02-13 21:46:26 -0500, André Thieme
<address.good.un...@justmail.de> said:

> We were talking about recur and its advantages. The issue was not about
> performance.

[snip]


> (loop [r (long 0) n (long 12345678)] (if (zero? n) r (recur (+ r n) (dec n))))

The point is that there is no advantage to recur. It's a wart
necessitated by the jvm's lack of tail call optimization.

In any conforming scheme it's just a plain old recursive call. In any
conforming common lisp it's just a simple loop. And in most decent
common lisps, as long as you're not optimizing for debug to keep the
stack frames, if you really want to wear the scheme recursive hair
shirt, you can do it recursively too:

(labels ((loop (r n) (if (zerop n) r (loop (+ r n) (1- n)))))
(loop 0 12345678))

but of course a real loop, not recursion, is the common lisp norm:

(loop for i upto 12345678 summing i)


--
Raffael Cavallaro, Ph.D.

John Thingstad

unread,
Feb 14, 2009, 1:29:02 AM2/14/09
to
På Sat, 14 Feb 2009 05:06:45 +0100, skrev Raffael Cavallaro
<raffaelc...@pas.espam.s.il.vous.plait.mac.com>:

>
> but of course a real loop, not recursion, is the common lisp norm:
>
> (loop for i upto 12345678 summing i)
>
>

Depend what you call norm. (let ((j 0)) (dotimes (i 12345678 j) (incf j
i))) seems less offensive to loop haters. Some swear Lisp should be purely
functional. So pehaps series? ;) The style wars will never end.. I would
guess there are as many styles as Lispers. The developers at Clojure
dislike that you can mix and match styles. To me it is one of the things
that attracts me to the language.

--------------
John Thingstad

William James

unread,
Feb 14, 2009, 2:32:29 AM2/14/09
to
Raffael Cavallaro wrote:

> On 2009-02-13 14:41:14 -0500, André Thieme
> <address.good.un...@justmail.de> said:
>
> > we can do this in Clojure:
> > ((fn [n] (loop [x n, res 0]
> > (if (zero? x) res (recur (dec x) (+ x res)))))
> > 12345678)
>
> we can do this in common lisp:
>
> ((lambda (n) (loop for x from n downto 0 summing x)) 12345678)

Reduce:

for n:=1:12345678 sum n;

76207888812681


Marco Antoniotti

unread,
Feb 14, 2009, 2:49:44 AM2/14/09
to
On Feb 14, 8:32 am, "William James" <w_a_x_...@yahoo.com> wrote:
> Raffael Cavallaro wrote:
> > On 2009-02-13 14:41:14 -0500, André Thieme
> > <address.good.until.2009.may...@justmail.de> said:
>
> > > we can do this in Clojure:
> > > ((fn [n] (loop [x n, res 0]
> > >            (if (zero? x) res (recur (dec x) (+ x res)))))
> > >  12345678)
>
> > we can do this in common lisp:
>
> > ((lambda (n) (loop for x from n downto 0 summing x)) 12345678)
>
> Reduce:
>
> for n:=1:12345678 sum n;
>
> 76207888812681

In Reduce it should be

for n := 1:12345678 sum n;

In CL it is

(loop for n from 1 below 12345678 sum n)

Longer, but same thing.

Cheers
--
Marco

William James

unread,
Feb 14, 2009, 3:09:22 AM2/14/09
to
Raffael Cavallaro wrote:

> but of course a real loop, not recursion, is the common lisp norm:
>
> (loop for i upto 12345678 summing i)

Ruby:

sum=0;(1..12345678).each{|n| sum+=n}

Alexander Lehmann

unread,
Feb 14, 2009, 4:13:16 AM2/14/09
to

(/ (* 12345678 12345679) 2)

Longer, but faster :)

Slobodan Blazeski

unread,
Feb 14, 2009, 5:18:06 AM2/14/09
to
That's far worse than Raffael solution.Why do you need so many tokens
for such a simple thing?
Try this:
J:
+/i.12345679
bobi

Slobodan Blazeski

unread,
Feb 14, 2009, 5:21:00 AM2/14/09
to
On Feb 14, 10:13 am, Alexander Lehmann <lehma...@in.tum.de> wrote:
> William James wrote:
> > Raffael Cavallaro wrote:
>
> >> On 2009-02-13 14:41:14 -0500, André Thieme
> >> <address.good.until.2009.may...@justmail.de> said:
>
> >>> we can do this in Clojure:
> >>> ((fn [n] (loop [x n, res 0]
> >>>            (if (zero? x) res (recur (dec x) (+ x res)))))
> >>>  12345678)
> >> we can do this in common lisp:
>
> >> ((lambda (n) (loop for x from n downto 0 summing x)) 12345678)
>
> > Reduce:
>
> > for n:=1:12345678 sum n;
>
> (/ (* 12345678 12345679) 2)
>
> Longer, but faster :)

Cheater :)

cheers
bobi

Alex Mizrahi

unread,
Feb 14, 2009, 6:56:35 AM2/14/09
to
??>> oh yes, it is very healthy (for a functional language!) to have
??>> "recur" special operator instead of normal tail recursion because of
??>> JVM deficiency.

AT> It's true, it's healthy to have recur.
AT> I agree that it is not nice that the JVM does not have tail recursion
AT> yet. But in practice that is not a real issue, as recur is available.

this reminds me a soviet anecdote: when there is no meat in the butcher's
shop (it was often the case)
they place an announcement at the shop's door: "there is no necessity in a
meat today".
are you as brainwashed as soviet's?

recursion is not limited to the cases when you just call current function in
a tail-call fashion. in functional
programming often you need to call function in an alternating fashion -- foo
calls bar, bar calls foo back
etc. and recur simply cannot do this. it can only emulate simple loops.

educate yourself: http://en.wikipedia.org/wiki/Mutual_recursion

AT> Let's look at this unreadable mess in CL:

AT> (DEFUN Y (F)
AT> ( (LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G G)) H)))
AT> #'(LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G G))
AT> H)))))

this is a piece of cheap propaganda, i can't believe you're writing this
seriously.
you've tried hard to make it looks like an unreadable mess, and now you
claim it is unreadable mess.
whoa. NOBODY WRITES CODE LIKE THIS.

if you need to do recursive call of a local function in CL, use labels.

AT> (FUNCALL (Y #'(LAMBDA (FN)
AT> #'(LAMBDA (X)
AT> (IF (ZEROP X) 0 (+ X (FUNCALL FN (- X 1)))))))
AT> 200)
AT> ==> 20100

it is not tail recursive, btw. tail recursive version with labels:

(labels ((sum (x acc) (if (zerop x)
acc
(sum (- x 1) (+ acc x)))))
(sum 200 0))

AT> we can do this in Clojure:
AT> ((fn [n] (loop [x n, res 0]
AT> (if (zero? x) res (recur (dec x) (+ x res)))))
AT> 12345678)

AT> No Y-Combinator needed, much shorter, much more pleasant for the eye.

no Y combinator is needed in Common Lisp either, and code is quite similar
when you
remove it. you might argue that anonimous functions/loops are somehow
better, but i
find named ones more readable.

what would be even "more pleasant for the eye": (loop for i from 1 to n
summing i)

AT> Plus: this call works.
AT> Stack overflow I guess.

sure it does, because it was not tail calls. you should admit you know very
little
about functional programming, as you do not know even basics.

AT> When I gave the argument of 2000 in clisp on Windows it crashed.

CLISP does not optimize tail calls in debug mode, iirc.
in SBCL it works fine with in default mode:

CL-USER> (labels ((sum (x acc) (if (zerop x)
acc
(sum (- x 1) (+ acc x)))))
(sum 20000000 0))

200000010000000

AT> Or another example:
AT> (defun fibonacci (n)
AT> (labels ((fibo-helper (x result)
AT> (if (zerop x)
AT> result
AT> (fibo-helper (1- x) (* x result)))))
AT> (fibo-helper n 1)))

wow, so now you know about labels and tail recursion. good for you.
it is not fibonacci, it is factorial, btw.

AT> This would also allow us to eliminate the function name we gave to fn.

you say it like it is some unique feature of Clojure. with a simple macro
you can make recur like in Clojure in CL. but you cannot add support for
tail calls in Clojure in any way, until they fix it in JVM.

AT> This makes it again shorter. And if we want to, we can put in a loop:
AT> (defn fibonacci [n]
AT> (loop [x n result 1]
AT> (if (zero? x)
AT> result
AT> (recur (dec x) (* x result)))))

AT> And although I am doing CL since 6 years and Clojure just 4 months or
AT> so, this looks cleaner in my opinion than the CL version.

than version you've wrote in CL. i would write it like this:

(defun factorial (n)
(loop with p = 1
for i from 1 to n
do (setf p (* p i))
finally (return p)))

IMHO that's cleaner. or if i absolutely must use recursion:

(defun factorial (n &optional (result 1))
(if (zerop n)
result
(factorial (- n 1) (* n result))))

it is even shorter than your Clojure thing.

AT> However, I also like to look at this from a more practical side.
AT> Clojure is usable now.

many people find PHP usable and mature -- it does not bother them
that it lacks important features or that it often gets incompatible changes.

Alexander Lehmann

unread,
Feb 14, 2009, 7:13:59 AM2/14/09
to

SCNR *g*

André Thieme

unread,
Feb 14, 2009, 7:15:10 AM2/14/09
to
Raffael Cavallaro schrieb:

> On 2009-02-13 21:46:26 -0500, André Thieme
> <address.good.un...@justmail.de> said:
>
>> We were talking about recur and its advantages. The issue was not about
>> performance.
> [snip]
>> (loop [r (long 0) n (long 12345678)] (if (zero? n) r (recur (+ r n)
>> (dec n))))
>
> The point is that there is no advantage to recur. It's a wart
> necessitated by the jvm's lack of tail call optimization.

I see your point, however, I personally disagree.
First of all, I would like to mention that recur also has no disadvantages.
That is not enough to introduce something into a language. Otherwise one
could add recur1, recur2, ... recur24637, ...
They all would have no disadvantages (assuming they work exactly like
recur).
But the fact that the JVM currently does not support tail call opt.
under the hood is a very plausible reason to add it.
Besides that, recur does in fact have three advantages:
1. it allows anon functions to call recurse, they can call themselves
2. it blocks you from accidently not doing tail calls, because the
compiler can check if recur really is in the tail position
3. as a minor advantage I want to add, that it is easy to search in code
for occurrences of “recur”, or if you spot it somewhere while flying
over your sources, you immediately see “Ah, here we have a recursive
call”.

So even when the JVM will support one day tail call optimization, recur
will not go.
Jon Harrop indicated that this (adding TCO) is happening at the moment
for the OpenJDK, and as Sun works on their own functional programming
language “Fortress” I suppose they will also be interested in adding it.
I will definitly continue to use recur. What I will stop to use are
trampolines.


> but of course a real loop, not recursion, is the common lisp norm:
>
> (loop for i upto 12345678 summing i)

Yes, loop is nice. In principle it’s what I would do in CL as well,
although for this specific case I would really prefer to directly
calculate that number via (defn sum [n] (+ (/ n 2) (/ (* n n) 2))).
In Clojure I could do:
(apply + (range 12345678)))

or use reduce instead of apply.

André Thieme

unread,
Feb 14, 2009, 7:52:32 AM2/14/09
to
Alex Mizrahi schrieb:

> recursion is not limited to the cases when you just call current function in
> a tail-call fashion. in functional
> programming often you need to call function in an alternating fashion -- foo
> calls bar, bar calls foo back
> etc. and recur simply cannot do this. it can only emulate simple loops.
>
> educate yourself: http://en.wikipedia.org/wiki/Mutual_recursion

For that case Clojure offers trampolines.
Yes, one can argue that trampolines are a specific idiom which one needs
to learn. It’s true. But let us not forget that tail recursion itself
already is such an idiom. It is an optimization hack.
No programming system currently allows you to write recursion as in a
way that is closest to math.
We always need to apply the design pattern “Tail recursion”.
So there already is something complex that one needs to do. Now what is
different in Clojure is, that you don’t make the calll
(fun 1 2 3) but instead
#(fun 1 2 3) when you use trampolines, and it is stack-safe again.
It’s not that hard to remember. But yes, it is one key stroke more complex.
Still I would stop using trampolines as soon the JVM will introduce that
kind of optimization. I will however continue to use recur, for the
typical recursion, which makes like 99% of all recursion I usually use.


> AT> Let's look at this unreadable mess in CL:
>
> AT> (DEFUN Y (F)
> AT> ( (LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G G)) H)))
> AT> #'(LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G G))
> AT> H)))))
>
> this is a piece of cheap propaganda, i can't believe you're writing this
> seriously.
> you've tried hard to make it looks like an unreadable mess, and now you
> claim it is unreadable mess.
> whoa. NOBODY WRITES CODE LIKE THIS.

Well, it’s from Kent Pitman.
http://www.nhplace.com/kent/Papers/Technical-Issues.html


> if you need to do recursive call of a local function in CL, use labels.
>
> AT> (FUNCALL (Y #'(LAMBDA (FN)
> AT> #'(LAMBDA (X)
> AT> (IF (ZEROP X) 0 (+ X (FUNCALL FN (- X 1)))))))
> AT> 200)
> AT> ==> 20100
>
> it is not tail recursive, btw. tail recursive version with labels:
>
> (labels ((sum (x acc) (if (zerop x)
> acc
> (sum (- x 1) (+ acc x)))))
> (sum 200 0))

Where is the anonymous function here?
recur allows you anon functions to call themself.


> what would be even "more pleasant for the eye": (loop for i from 1 to n
> summing i)

Yes true, for that specific task it is much nicer.
I like it nearly as much as (apply + (range n)).

> AT> Plus: this call works.
> AT> Stack overflow I guess.
>
> sure it does, because it was not tail calls. you should admit you know very
> little about functional programming, as you do not know even basics.

I see no reason to admit that at this point, as I was fully aware about
this. What I was doing was talking about anon functions calling themselves.

> AT> Or another example:
> AT> (defun fibonacci (n)
> AT> (labels ((fibo-helper (x result)
> AT> (if (zerop x)
> AT> result
> AT> (fibo-helper (1- x) (* x result)))))
> AT> (fibo-helper n 1)))
>
> wow, so now you know about labels and tail recursion. good for you.
> it is not fibonacci, it is factorial, btw.

Yes right, how embarrassing :-/


> AT> This would also allow us to eliminate the function name we gave to fn.
>
> you say it like it is some unique feature of Clojure. with a simple macro
> you can make recur like in Clojure in CL. but you cannot add support for
> tail calls in Clojure in any way, until they fix it in JVM.

Let’s say you have a Lisp with no support for TCO.
Then you would also be forced to wait until your vendor fixes it.
Or you have an open source solution and do it yourself (as it currently
happens with the OpenJDK).
And of course one can add recur also in CL. Everything that Clojure has
can be done in CL and vice versa.


> AT> This makes it again shorter. And if we want to, we can put in a loop:
> AT> (defn fibonacci [n]
> AT> (loop [x n result 1]
> AT> (if (zero? x)
> AT> result
> AT> (recur (dec x) (* x result)))))
>
> AT> And although I am doing CL since 6 years and Clojure just 4 months or
> AT> so, this looks cleaner in my opinion than the CL version.
>
> than version you've wrote in CL. i would write it like this:
>
> (defun factorial (n)
> (loop with p = 1
> for i from 1 to n
> do (setf p (* p i))
> finally (return p)))
>
> IMHO that's cleaner.

After some months of functional programming I don’t like code with setf
anymore. But your solution is working perfectly, and it’s only my taste,
not a technical issue.

In Clojure I would probably do:
(defn factorial [n] (apply * (range 1 (inc n))))

or if I want the set of all factorials, then
(def fibs (lazy-cat [0 1] (map + fibs (drop 1 fibs))))

This is my favourite. No function needed, just a variable bound to an
infinite lazy sequence.
This also has the nice advantage of being automatically memoized.


> or if i absolutely must use recursion:
>
> (defun factorial (n &optional (result 1))
> (if (zerop n)
> result
> (factorial (- n 1) (* n result))))
>
> it is even shorter than your Clojure thing.

It’s true, but I don’t like this solution at all, because it introduces
an optional argument which can’t be used at the user site.
Peter Seibel calls this a leaky abstraction.
For hobby programming it is okay, but a clean solution would be
(defun factorial (n)
(labels ((helper (n result)
(if (zerop n)
result
(helper (1- n) (* result n)))))
(helper n 1)))

Slobodan Blazeski

unread,
Feb 14, 2009, 8:42:38 AM2/14/09
to

There was a joke about NASA(or RFSA) spending a lot of money for pens
that could write in zero gravity. When they announced their
masterpiece reporters asked them why they don't use pencils?

cheers
bobi

Slobodan Blazeski

unread,
Feb 14, 2009, 8:59:44 AM2/14/09
to
On Feb 14, 12:56 pm, "Alex Mizrahi" <udode...@users.sourceforge.net>
wrote:

>  ??>> oh yes, it is very healthy (for a functional language!) to have
>  ??>> "recur" special operator instead of normal tail recursion because of
>  ??>> JVM deficiency.
>
>  AT> It's true, it's healthy to have recur.
>  AT> I agree that it is not nice that the JVM does not have tail recursion
>  AT> yet. But in practice that is not a real issue, as recur is available.
>
> this reminds me a soviet anecdote: when there is no meat in the butcher's
> shop (it was often the case)
>  they  place an announcement at the shop's door: "there is no necessity in a
> meat today".
> are you as brainwashed as soviet's?
I'm afraid he is. If you still feel the urge to reason with him read
his posts in Making Lisp Popular thread.
http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/be5630a07e123df2/86c5dd4ef015ef37?#86c5dd4ef015ef37

>
> recursion is not limited to the cases when you just call current function in
> a tail-call fashion. in functional
> programming often you need to call function in an alternating fashion -- foo
> calls bar, bar calls foo back
> etc. and recur simply cannot do this. it can only emulate simple loops.
>
> educate yourself:http://en.wikipedia.org/wiki/Mutual_recursion
>
>  AT> Let's look at this unreadable mess in CL:
>
>  AT> (DEFUN Y (F)
>  AT>   (  (LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G  G)) H)))
>  AT>    #'(LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G  G))
>  AT> H)))))
>
> this is a piece of cheap propaganda, i can't believe you're writing this
> seriously.
> you've tried hard to make it looks like an unreadable mess, and now you
> claim it is unreadable mess.
> whoa. NOBODY WRITES CODE LIKE THIS.
Above is just a Kent Pittman historical perspective code taken out of
context.The more I read Mr Thieme posts the more I believe that he has
a spamming frog disorder, uncurable desease I'm afraid.


cheers
bobi

Raffael Cavallaro

unread,
Feb 14, 2009, 9:54:11 AM2/14/09
to
On 2009-02-14 07:15:10 -0500, André Thieme
<address.good.un...@justmail.de> said:

> First of all, I would like to mention that recur also has no disadvantages.

It can't be used for mutual recursion. Common Lisp's labels can;
scheme's letrec can. Clojure needs a separate piece of
work-around-the-jvm syntax for mutual recursion.

--
Raffael Cavallaro, Ph.D.

Alex Mizrahi

unread,
Feb 14, 2009, 10:34:29 AM2/14/09
to
AT> First of all, I would like to mention that recur also has no
AT> disadvantages. .... But the fact that the JVM currently does not
support tail call opt.

if you compare Clojure without recur to Clojure with recur, obviously recur
will be seen as an advantage. but if you compare Clojure to CL, you will
find recur quite limited, and thus having recur INSTEAD of recursion is a
disadvantage.

AT> or if you spot it somewhere while flying
AT> over your sources, you immediately see “Ah, here we have a
recursive
AT> call”.

it is highly subjective, it seems to me that recur is less readable because
it
makes harder to see what is recur'ed -- one might think it calls outer
function
while it calls inner. typically explicit is better than implicit.


Alex Mizrahi

unread,
Feb 14, 2009, 10:38:29 AM2/14/09
to
AT> First of all, I would like to mention that recur also has no
AT> disadvantages.

btw, we might compare "recur" to a compiler that will automatically
detects such cases of recursion and optimizes them accordingly.
(i believe most compilers do this, even C ones). then one might argue
that recur has a disadvantage making code less readable, as it might
be hard to see what is called


André Thieme

unread,
Feb 14, 2009, 10:44:33 AM2/14/09
to
Slobodan Blazeski schrieb:

> On Feb 14, 12:56 pm, "Alex Mizrahi" <udode...@users.sourceforge.net>
> wrote:
>> ??>> oh yes, it is very healthy (for a functional language!) to have
>> ??>> "recur" special operator instead of normal tail recursion because of
>> ??>> JVM deficiency.
>>
>> AT> It's true, it's healthy to have recur.
>> AT> I agree that it is not nice that the JVM does not have tail recursion
>> AT> yet. But in practice that is not a real issue, as recur is available.
>>
>> this reminds me a soviet anecdote: when there is no meat in the butcher's
>> shop (it was often the case)
>> they place an announcement at the shop's door: "there is no necessity in a
>> meat today".
>> are you as brainwashed as soviet's?
> I'm afraid he is. If you still feel the urge to reason with him read
> his posts in Making Lisp Popular thread.
> http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/be5630a07e123df2/86c5dd4ef015ef37?#86c5dd4ef015ef37

I don’t understand why you are so angry.
You said some bullshit and were not able to defend it. Instead of going
back and correct yourself, such as:
"Ok ok, sorry. I wanted to say that it is easier to *implement* Lisps in
their own VM instead of the JVM".
But no, you need to stick with the word "better", although this makes no
sense at all.
I see that you are lurking around here since long time, abusing other
people, claiming that they know *nothing* about X and Y, even when this
is not correct. Why not trying to be more rational?


>> recursion is not limited to the cases when you just call current function in
>> a tail-call fashion. in functional
>> programming often you need to call function in an alternating fashion -- foo
>> calls bar, bar calls foo back
>> etc. and recur simply cannot do this. it can only emulate simple loops.
>>
>> educate yourself:http://en.wikipedia.org/wiki/Mutual_recursion
>>
>> AT> Let's look at this unreadable mess in CL:
>>
>> AT> (DEFUN Y (F)
>> AT> ( (LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G G)) H)))
>> AT> #'(LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G G))
>> AT> H)))))
>>
>> this is a piece of cheap propaganda, i can't believe you're writing this
>> seriously.
>> you've tried hard to make it looks like an unreadable mess, and now you
>> claim it is unreadable mess.
>> whoa. NOBODY WRITES CODE LIKE THIS.
> Above is just a Kent Pittman historical perspective code taken out of
> context.

OMG, the context is that recur allows anon functions to call themselves.
The y-combinator also allows this. This absolutely *is* context.


> The more I read Mr Thieme posts the more I believe that he has
> a spamming frog disorder, uncurable desease I'm afraid.

Again, nonsense.
You are angry and aggressive, and it simply is not needed.
This is a phenomenon that I observe with many people when they are out
of arguments. I invite you to join rational discussions about Lisp.
No reason to feel unsecure when a new and promising Lisp dialect shows up.
*That* would be really being brainwashed.
Defending CL for every price, no matter how bad it sucks.

Marco Antoniotti

unread,
Feb 14, 2009, 11:06:13 AM2/14/09
to
On Feb 14, 1:52 pm, André Thieme <address.good.until.
> Well, it’s from Kent Pitman.http://www.nhplace.com/kent/Papers/Technical-Issues.html

As in

(defun factorial (n) (apply '* (iota 1 n)))

It isn't very compelling...

> or if I want the set of all factorials, then
> (def fibs (lazy-cat [0 1] (map + fibs (drop 1 fibs))))
>
> This is my favourite. No function needed, just a variable bound to an
> infinite lazy sequence.
> This also has the nice advantage of being automatically memoized.

As in

(defparameter fibs (streams:cat 0 1 (streams:map-stream '+ fibs
(streams:tail fibs)))

... again this is not very compelling either. This is SICP.

Cheers
--
Marco


André Thieme

unread,
Feb 14, 2009, 11:17:13 AM2/14/09
to
Alex Mizrahi schrieb:

> AT> First of all, I would like to mention that recur also has no
> AT> disadvantages. .... But the fact that the JVM currently does not
> support tail call opt.
>
> if you compare Clojure without recur to Clojure with recur, obviously recur
> will be seen as an advantage. but if you compare Clojure to CL, you will
> find recur quite limited, and thus having recur INSTEAD of recursion is a
> disadvantage.

Yes, Clojure with recur (TCO) vs Clojure without it (no TCO) looks better.
However, in my opinion, if I have to chose between:
a) Clojure with recur on a JVM that supports TCO
vs
b) Clojure without recur on a JVM that supports TCO,

then I vote for a).


> AT> or if you spot it somewhere while flying
> AT> over your sources, you immediately see "Ah, here we have a
> recursive
> AT> call".
>
> it is highly subjective, it seems to me that recur is less readable because
> it makes harder to see what is recur'ed -- one might think it calls outer
> function while it calls inner. typically explicit is better than implicit.

Recur jumps always to the most inner point, so it is always clear at the
first glimpse to where it will recur.
Also the compiler will make sure that one uses it only in tail position.
To me it happened already that I got an error message of not using recur
in TP.

Without talking specifically to you Alex, I observed that some CLers try
to jump on the recur issue, as this is in their eyes the weakest point.
The ironical thing about this is that at leats to my knowledge 100% of
those people actually never programmed in Clojure, and experience them-
selves how good/bad the situation really is.

While I fully agree that it is a weak point of the JVM that it does not
support TCO yet, I did not stumble upon any problematic situation yet.
Even when I asked people for creating artificial examples that can not
be easily solved with recur/trampolines, I did not see anything yet.

Lisp really has no easy stand today as it seems.
People see that it has a lot of parens and without trying it, they de-
cide it is nothing for them.
Clojure now reduced the amount of parens, but people now hear about
tail calls and argue that Clojure Lisp is not for them. What I find a
bit sad is that most of them are CLers.

Anyway, thanks for letting me know your opinions about recur.

Tamas K Papp

unread,
Feb 14, 2009, 11:27:52 AM2/14/09
to
On Sat, 14 Feb 2009 17:17:13 +0100, André Thieme wrote:

> Lisp really has no easy stand today as it seems. People see that it has
> a lot of parens and without trying it, they de- cide it is nothing for
> them.
> Clojure now reduced the amount of parens, but people now hear about tail
> calls and argue that Clojure Lisp is not for them. What I find a bit sad
> is that most of them are CLers.

I have only had cursory look at Clojure, so I don't know much about
it. My guess is that both Clojure/CL would make it into the top 5
languages for most CL/Clojure users. So one might prefer CL over
Clojure and argue about tail calls etc, but still strongly prefer
Clojure to, say, Java or (horribile dictu) F#. Given this, I don't
see anything sad with people preferring Clojure/CL over CL/Clojure --
not all changes are innovations for everyone.

Tamas

Alex Mizrahi

unread,
Feb 14, 2009, 11:31:22 AM2/14/09
to
AT> (fun 1 2 3) but instead
AT> #(fun 1 2 3) when you use trampolines, and it is stack-safe again.

how does it work? does it inline fun instead of calling it, or what?
if it works via inlining, that it is not a solution..

AT> Still I would stop using trampolines as soon the JVM will
AT> introduce that kind of optimization.

if trampolines somehow automagically fix the problem with "one
keystroke", then why compiler cannot detect such situation and insert
these "#"? it seems you're not saying all the truth.. ot Clojure's compiler
is dumb

??>> NOBODY WRITES CODE LIKE THIS.

AT> Well, it's from Kent Pitman.
AT> http://www.nhplace.com/kent/Papers/Technical-Issues.html

it was an example of code that is not elegant in Lisp2, this is not
a code in CL's style. btw, i haven't seen any practical use of Y combinator
so far, even in Haskell they just use their analog of LABELS.


??>> (labels ((sum (x acc) (if (zerop x)
??>> acc
??>> (sum (- x 1) (+ acc x)))))
??>> (sum 200 0))

AT> Where is the anonymous function here?
AT> recur allows you anon functions to call themself.

and why do we need one here? code is pretty readable as it is.

we could make anonymous functions with recur in CL
with a simple macro like this:

(defmacro lambdarec (args &body body)
`(labels ((recur (,@args) ,@body))
(function recur)))

and it will work just like in Clojure:

(funcall (lambdarec (x accum)
(if (zerop x)
accum
(recur (- x 1) (+ accum x))))
200 0)

(except in function position, unfortunately CL does not macroexpand it).
but i don't think this is even remotely useful..

??>> sure it does, because it was not tail calls. you should admit you know
??>> very little about functional programming, as you do not know even
??>> basics.

AT> I see no reason to admit that at this point, as I was fully aware about
AT> this.

why did you complain about stack overflow then? either you're an idiot,
or you're going to treat me like an idiot, i see no other possibilities
here.

if you write random bullshit like this into newsgroups, you'll get no
respect.

AT> What I was doing was talking about anon functions calling themselves.

"anon functions calling themselves" is not a benefit of itself, it is not
obvious
that it allows one to solve real programming tasks even a bit better.

AT> Let's say you have a Lisp with no support for TCO.
AT> Then you would also be forced to wait until your vendor fixes it.

if Lisp does not have support for TCO, then probably it has good support
for imperative programming -- like tagbody and mutable variables, that can
be used to implement quite a hairy stuff.

as i understand, Clojure does not have support for imperative programming
constructs, and neither it has good support for functional programming
constructs.
this makes me think that it is not such a good language.

AT> And of course one can add recur also in CL. Everything that Clojure has
AT> can be done in CL and vice versa.

emm, we can add recur from CL itself, but you cannot add TCO from Clojure
itself, can you?

perhaps you can write some smart compiler that will do it for you, but
that would be really hard (and thus impractical), while adding recur into CL
is trivial.

AT> or if I want the set of all factorials, then
AT> (def fibs (lazy-cat [0 1] (map + fibs (drop 1 fibs))))

AT> This is my favourite.

why don't you use Haskell then? :)

AT> After some months of functional programming I don't like code with setf
AT> anymore.

one can do it with internal LOOP's variable rebinding feature, which i think
is semantically equivalent to recur's variable rebinding:

(loop for i from 1 to n

for result = 1 then (* result i)
finally (return result))

André Thieme

unread,
Feb 14, 2009, 11:40:40 AM2/14/09
to
Marco Antoniotti schrieb:

>> In Clojure I would probably do:
>> (defn factorial [n] (apply * (range 1 (inc n))))
>
> As in
>
> (defun factorial (n) (apply '* (iota 1 n)))
>
> It isn't very compelling...
>
>> or if I want the set of all factorials, then
>> (def fibs (lazy-cat [0 1] (map + fibs (drop 1 fibs))))
>>
>> This is my favourite. No function needed, just a variable bound to an
>> infinite lazy sequence.
>> This also has the nice advantage of being automatically memoized.
>
> As in
>
> (defparameter fibs (streams:cat 0 1 (streams:map-stream '+ fibs
> (streams:tail fibs)))
>
> ... again this is not very compelling either. This is SICP.

All of my examples can of course be translated into CL, Scheme or
other turing complete programming langauges.
I would not be surprised if all CL programs could be translated into
Clojure as well.

Anyway, I see the strong point of Clojure in offering all these tools
in the core language. This means that everyone will use them.
The iota function has, no idea, 92 users in the world.
Probably 7 who use it regularily.
(I don’t mean these numbers literally, but I have not seen that anyone
used those, neither in open source, nor in my professional work).

If something ships with the language then in most cases everyone
will use that, instead of trying an alternative library.
Now there are 26 pattern matching libs for CL, and they have an
accumulated number of 17 users worldwide.
CLers like if the language offers already good bit of reusable code.
Otherwise they may have become Scheme users.
Clojure goes some steps forward. Unlike CL it is not bound to limit
itself with the functions defined by the HS.
Those functions are available to all Clojure developers, and there is
a big agreement to use them. This makes code and libs more compatible
and much easier to read.

Aatu Koskensilta

unread,
Feb 14, 2009, 11:37:36 AM2/14/09
to
"Alex Mizrahi" <udod...@users.sourceforge.net> writes:

> btw, i haven't seen any practical use of Y combinator so far, even
> in Haskell they just use their analog of LABELS.

What is the Haskell analogue of LABELS?

--
Aatu Koskensilta (aatu.kos...@uta.fi)

"Wovon man nicht sprechen kann, darüber muss man schweigen"
- Ludwig Wittgenstein, Tractatus Logico-Philosophicus

Tobias C. Rittweiler

unread,
Feb 14, 2009, 11:46:46 AM2/14/09
to
Aatu Koskensilta <aatu.kos...@uta.fi> writes:

> What is the Haskell analogue of LABELS?

A better question would be what is the Haskell analogue of FLET? :-)

-T.

André Thieme

unread,
Feb 14, 2009, 12:14:33 PM2/14/09
to
Alex Mizrahi schrieb:

> AT> (fun 1 2 3) but instead
> AT> #(fun 1 2 3) when you use trampolines, and it is stack-safe again.
>
> how does it work? does it inline fun instead of calling it, or what?
> if it works via inlining, that it is not a solution..

http://code.google.com/p/clojure/source/browse/trunk/src/clj/clojure/core.clj?=1276#3744


> AT> Still I would stop using trampolines as soon the JVM will
> AT> introduce that kind of optimization.
>
> if trampolines somehow automagically fix the problem with "one
> keystroke", then why compiler cannot detect such situation and insert
> these "#"? it seems you're not saying all the truth.. ot Clojure's compiler
> is dumb

http://groups.google.com/group/clojure/msg/3addf875319c5c10


> AT> I see no reason to admit that at this point, as I was fully aware about
> AT> this.
>
> why did you complain about stack overflow then? either you're an idiot,
> or you're going to treat me like an idiot, i see no other possibilities
> here.

No problem, I will tell you what the other possibility is:
I was making a sarcastic comment and you missed that. I gave an example
about how an anon function can call itself.


> if you write random bullshit like this into newsgroups, you'll get no
> respect.

I am sure that with "this" you were referring to your own sentence.
Btw, I am not here for getting respect from random people on c.l.l.
Some people though need that... at least one source of respect and
attention.
(not referring to you!)


> if Lisp does not have support for TCO, then probably it has good support
> for imperative programming -- like tagbody and mutable variables, that can
> be used to implement quite a hairy stuff.

Yes, for CL it’s good.
And even today some small programs can be written in imperative style.


> as i understand, Clojure does not have support for imperative programming
> constructs, and neither it has good support for functional programming
> constructs.
> this makes me think that it is not such a good language.

Ah oki, you never tried Clojure and heared some rumors somewhere.
It’s our human property to build up an opinion, although we don’t know
enough facts to have the right to have an opinion.
Just see the people who look into Lisp. Only a minority like us stayed.

I have no problems at all if people don’t like Clojure. I personally
just like it way more than CL. Basically every problem in my
professional programming I can solve in a more productive way. And this
does not always have to do with the sheer mount of code available for
the JVM.
But CL stays on rank 2, with a noticable distance to 1.
If Rich continues to work so hard on Clojure, it will soon outnumber the
CL users. Evolution in progress.


> AT> And of course one can add recur also in CL. Everything that Clojure has
> AT> can be done in CL and vice versa.
>
> emm, we can add recur from CL itself, but you cannot add TCO from Clojure
> itself, can you?
> perhaps you can write some smart compiler that will do it for you, but
> that would be really hard (and thus impractical), while adding recur into CL
> is trivial.

Yes.
I think it makes most sense to just use what is available today.
I have not met on person who used Clojure for a few months who complained
about any problems with recur or recursion in Clojure.
It is much more a fantasy issue of people who actually never
touched Clojure. Since I use it every day (be it professionally
or for private stuff) I find no way of going back to CL, which
by now feels really old to me.


> AT> or if I want the set of all factorials, then
> AT> (def fibs (lazy-cat [0 1] (map + fibs (drop 1 fibs))))
>
> AT> This is my favourite.
>
> why don't you use Haskell then? :)

Because I can do it in Lisp, which I like.
Lisp can be used more practical, as so many libs from the JVM are available.
But Haskell is very cool, and I am glad that Clojure learned some
concepts from it. Makes sense.

André Thieme

unread,
Feb 14, 2009, 12:34:09 PM2/14/09
to
Tamas K Papp schrieb:

What you present sounds very realistic to me.
I myself still have a nice CL project (about AI) that I will continue
to develop with SBCL.

What I find sad is that some CL users have a really negative opinion
about Clojure.
I can understand it when looking at it from this POV:
since years I am reading c.l.l, and every few weeks someone jumps in
and explains us how to improve Lisp, how to make it more popular, how
to remove parens, and such. Every few months some guy presents his own
Lisp. Those new Lisps basically all suck very very bad.
When I read about Clojure, I did not look at it all all. Why should I?
Another guy who thinks he can produce something useful!

We, who we read c.l.l know these situations, and they "reprogrammed"
us, so that we automatically think a new dialect of Lisp just has to
be bullshit, like the 37 others were, that were presented in the last
years.

Out of curiosity I visited one day the Clojure homepage, after a friend
suggested me to do it. It took only moments to get me hooked.
I watched the movies on the website and read it completely, and began
to install/use it. Over the course of some days I read the sources of
Clojure and realized that for me personally it is what I was waiting
for.
Immediately I understood that Clojure is the ticket for thousands of
Lispers to get a Lisp job. For now that can be done by sneaking into
small Java companies and offer them the productivity of a Lisp in their
environment. Only a subset of those companies will agree on that. But
a subset of hundreds to thousands of Java companies per (big) city
is still substantial enough to have a realistic chance.

So, back to the issue:
by repeating over years again and again that other Lisps suck and that
CL rules some here came to believe this, as if it were an objective
fact.
Some see a "danger" in Clojure. As long it is not CL, it must be bad.
This is too religious for me. Heck, programming languages are tools.

Obviously, I can’t speak for all CLers. Several experts already use
Clojure and have a different POV as I have.
Clojure is Lisp, it is an evolutionary step, CL does not suddenly
become bad only because Clojure exists, and Lispers should not be
scared by it, or "fight" against it without even having tried it out
for some time.

William James

unread,
Feb 14, 2009, 1:57:11 PM2/14/09
to
André Thieme wrote:

> Besides that, recur does in fact have three advantages:
> 1. it allows anon functions to call recurse, they can call themselves
> 2. it blocks you from accidently not doing tail calls, because the
> compiler can check if recur really is in the tail position
> 3. as a minor advantage I want to add, that it is easy to search in
> code for occurrences of “recur”, or if you spot it somewhere while
> flying over your sources, you immediately see “Ah, here we have a
> recursive call”.

+1

ANS Forth uses "recurse":

: fac dup 2 < if drop 1 else dup 1- recurse * endif ;


>
> So even when the JVM will support one day tail call optimization,
> recur will not go.
> Jon Harrop indicated that this (adding TCO) is happening at the moment
> for the OpenJDK, and as Sun works on their own functional programming
> language “Fortress” I suppose they will also be interested in adding
> it. I will definitly continue to use recur. What I will stop to use
> are trampolines.
>
>
> > but of course a real loop, not recursion, is the common lisp norm:
> >
> > (loop for i upto 12345678 summing i)
>
> Yes, loop is nice. In principle it’s what I would do in CL as well,
> although for this specific case I would really prefer to directly
> calculate that number via (defn sum [n] (+ (/ n 2) (/ (* n n) 2))).
> In Clojure I could do:
> (apply + (range 12345678)))
>
> or use reduce instead of apply.

Wouldn't it be inefficient to create a list or array of size 12345678?

Slobodan Blazeski

unread,
Feb 14, 2009, 2:30:34 PM2/14/09
to
On Feb 14, 4:44 pm, André Thieme <address.good.until.

2009.may...@justmail.de> wrote:
> Slobodan Blazeski schrieb:
>
>
>
>
>
> > On Feb 14, 12:56 pm, "Alex Mizrahi" <udode...@users.sourceforge.net>
> > wrote:
> >>  ??>> oh yes, it is very healthy (for a functional language!) to have
> >>  ??>> "recur" special operator instead of normal tail recursion because of
> >>  ??>> JVM deficiency.
>
> >>  AT> It's true, it's healthy to have recur.
> >>  AT> I agree that it is not nice that the JVM does not have tail recursion
> >>  AT> yet. But in practice that is not a real issue, as recur is available.
>
> >> this reminds me a soviet anecdote: when there is no meat in the butcher's
> >> shop (it was often the case)
> >>  they  place an announcement at the shop's door: "there is no necessity in a
> >> meat today".
> >> are you as brainwashed as soviet's?
> > I'm afraid he is. If you still feel the urge to reason with him read
> > his posts in Making Lisp Popular thread.
> >http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/be...

>
> I don’t understand why you are so angry.
> You said some bullshit and were not able to defend it. Instead of going
> back and correct yourself, such as:
> "Ok ok, sorry. I wanted to say that it is easier to *implement* Lisps in
> their own VM instead of the JVM".
> But no, you need to stick with the word "better", although this makes no
> sense at all.
Yes it does.
http://www.mindspring.com/~mfpatton/sketch.htm

> I see that you are lurking around here since long time, abusing other
> people, claiming that they know *nothing* about X and Y, even when this
> is not correct. Why not trying to be more rational?
Rational? I don't want spammers and fundamentalists who can't defend
their positions with code, but instead are going for under the belt
tricks spread nonsense in this group. What could be more rational
then that? You don't want to hear anything except only the best about
Clojure. Ok Clojure has some nice things, I agree, but its a frickin'
language it has flaws too. And recur and stupid tarmplones are one of
them. Instead of writing nonsense go and help Hinley with fixing the
implementation. The problem could be fixed, you could use CPS or some
other technique that are used by implementations running on JVM but
still manage to use recursion without jumping through hoops.

>
>
>
> >> recursion is not limited to the cases when you just call current function in
> >> a tail-call fashion. in functional
> >> programming often you need to call function in an alternating fashion -- foo
> >> calls bar, bar calls foo back
> >> etc. and recur simply cannot do this. it can only emulate simple loops.
>
> >> educate yourself:http://en.wikipedia.org/wiki/Mutual_recursion
>
> >>  AT> Let's look at this unreadable mess in CL:
>
> >>  AT> (DEFUN Y (F)
> >>  AT>   (  (LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G  G)) H)))
> >>  AT>    #'(LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G  G))
> >>  AT> H)))))
>
> >> this is a piece of cheap propaganda, i can't believe you're writing this
> >> seriously.
> >> you've tried hard to make it looks like an unreadable mess, and now you
> >> claim it is unreadable mess.
> >> whoa. NOBODY WRITES CODE LIKE THIS.
> > Above is just a Kent Pittman historical perspective code taken out of
> > context.
>
> OMG, the context is that recur allows anon functions to call themselves.
> The y-combinator also allows this. This absolutely *is* context.
Bullshit. You show us code that nobody will write, except to
demonstrate some historical staff about various lisp dialects and
imply that lispers make such mess. Well we don't.

>
>  > The more I read Mr Thieme posts the more I believe that he has
>
> > a spamming frog disorder, uncurable desease I'm afraid.
>
> Again, nonsense.
> You are angry and aggressive, and it simply is not needed.
> This is a phenomenon that I observe with many people when they are out
> of arguments.
I'm sure they are when speaking with you.
Andre Thieme: The ocean is red.
Unlucky Person: It is usually blue.
Andre Thieme: No its red.
Unlucky Person:[Tries to explain that ocean is blue or blue-green]
Andre Thieme: No its red. Ever heard of red sea?
Unlucky Person:[Tries to explain again and put some supporting links ]
Andre Thieme:[Starts speaking bullshit for very long time while
several people try to put some light on his crup]
... [Several hours later]
Unlucky Person:[Gets angry]

>I invite you to join rational discussions about Lisp.
Then you have to make sense what you're talking about.You don't do
that now. You only care how many long posts should you make before
people get tired and stop correct you weak I wouldn't call them
arguments.

> No reason to feel unsecure when a new and promising Lisp dialect shows up.
New and promising Lisps show up every year. But they don't stay new
for long, nor promising.

> *That* would be really being brainwashed.
> Defending CL for every price, no matter how bad it sucks.
Its not cl that sucks. Change your diapers.

bobi

Slobodan Blazeski

unread,
Feb 14, 2009, 2:54:16 PM2/14/09
to
On Feb 13, 8:41 pm, André Thieme <address.good.until.
2009.may...@justmail.de> wrote:
> Alex Mizrahi schrieb:
>
> >  AT> 1) The connection to Java can be seen by some folks as very healthy.
>
> > oh yes, it is very healthy (for a functional language!) to have "recur"
> > special operator instead of normal tail recursion because of JVM deficiency.

>
> It’s true, it’s healthy to have recur.
> I agree that it is not nice that the JVM does not have tail recursion
> yet. But in practice that is not a real issue, as recur is available.
>
> Let’s look at this unreadable mess in CL:
>
> (DEFUN Y (F)

>   (  (LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G  G)) H)))
>    #'(LAMBDA (G) #'(LAMBDA (H) (FUNCALL (FUNCALL F (FUNCALL G  G)) H)))))

>
> (FUNCALL (Y #'(LAMBDA (FN)
>                  #'(LAMBDA (X)

>                      (IF (ZEROP X) 0 (+ X (FUNCALL FN (- X 1)))))))
>           200)
> ==> 20100

>
> we can do this in Clojure:
> ((fn [n] (loop [x n, res 0]
>             (if (zero? x) res (recur (dec x) (+ x res)))))
>   12345678)

>
> No Y-Combinator needed, much shorter, much more pleasant for the eye.
> Plus: this call works.

> When I gave the argument of 2000 in clisp on Windows it crashed.
> Stack overflow I guess.
>
> Or another example:
> (defun fibonacci (n)
>    (labels ((fibo-helper (x result)
>               (if (zerop x)
>                   result

>                   (fibo-helper (1- x) (* x result)))))
>      (fibo-helper n 1)))
This is code for factorial not fibonacci
(defun fact (n &optional (r 1))
(if (zerop n) r (fact (1- n) (* n r))))
This is fibonacci
(defun fib (n)
(if (< n 2) n
(+ (fib (1- n)) (fib (- n 2)))))

cheers
bobi

Raffael Cavallaro

unread,
Feb 14, 2009, 3:53:09 PM2/14/09
to
On 2009-02-14 11:17:13 -0500, André Thieme
<address.good.un...@justmail.de> said:

> Without talking specifically to you Alex, I observed that some CLers try
> to jump on the recur issue, as this is in their eyes the weakest point.
> The ironical thing about this is that at leats to my knowledge 100% of
> those people actually never programmed in Clojure, and experience them-
> selves how good/bad the situation really is.

I think the situation is a bit more nuanced than that. I for one have
used clojure and like it very much. But you're trying to make a virtue
of a necessity with loop/recur. It exists because the jvm doesn't do
tail call optimization. If the jvm did, then one would simply do
ordinary recursive calls, and/or clojure would have the equivalent of
scheme's letrec and named let.

The real innovations of clojure as a lisp dialect are:

1. the default functional semantics for data structures that are
traditionally mutable in lisp (conses, lists, hash maps, vectors) but
which are "persistent," aka, immutable, in clojure, in conjunction
with...

2. the use of stm for those data that are mutable, eliminating explicit
locking from user code.

These combine to make concurrency much easier to get right.[1] You
should be trumpeting these features, not loop/recur, which is only
there because the underlying platform doesn't do tail call optimization.

[1] Note that I haven't included "easy access to java and java
libraries" because there are a good number of scheme/lisp
implementations that run on the jvm and/or allow the use of java libs.
So access to java libs is not something that sets clojure apart from a
number of other lisp/scheme implementations.
--
Raffael Cavallaro, Ph.D.

Alex Mizrahi

unread,
Feb 14, 2009, 3:55:57 PM2/14/09
to
??>> btw, i haven't seen any practical use of Y combinator so far, even
??>> in Haskell they just use their analog of LABELS.

AK> What is the Haskell analogue of LABELS?

let .. in or where:

(labels ((sum (x acc) (if (zerop x) acc (sum (- x 1) (+ acc x))))
(sum 200 0)

main = let sum x acc = if x == 0 then acc else sum (x - 1) (acc + x)
in sum 200 0


main = sum 200 0
where sum x acc = if x == 0 then acc else sum (x - 1) (acc + x)

Marco Antoniotti

unread,
Feb 14, 2009, 4:04:14 PM2/14/09
to
On Feb 14, 5:40 pm, André Thieme <address.good.until.

2009.may...@justmail.de> wrote:
> Marco Antoniotti schrieb:
>
>
>
> >> In Clojure I would probably do:
> >> (defn factorial [n] (apply * (range 1 (inc n))))
>
> > As in
>
> >   (defun factorial (n) (apply '* (iota 1 n)))
>
> > It isn't very compelling...
>
> >> or if I want the set of all factorials, then
> >> (def fibs (lazy-cat [0 1] (map + fibs (drop 1 fibs))))
>
> >> This is my favourite. No function needed, just a variable bound to an
> >> infinite lazy sequence.
> >> This also has the nice advantage of being automatically memoized.
>
> > As in
>
> > (defparameter fibs (streams:cat 0 1 (streams:map-stream '+ fibs
> > (streams:tail fibs)))
>
> > ... again this is not very compelling either.  This is SICP.
>
> All of my examples can of course be translated into CL, Scheme or
> other turing complete programming langauges.
> I would not be surprised if all CL programs could be translated into
> Clojure as well.

From what I saw I woul dnot be surprised.

> Anyway, I see the strong point of Clojure in offering all these tools
> in the core language. This means that everyone will use them.
> The iota function has, no idea, 92 users in the world.
> Probably 7 who use it regularily.
> (I don’t mean these numbers literally, but I have not seen that anyone
>   used those, neither in open source, nor in my professional work).

Maybe. There is definitively a lot of advantages in "standards".
Maybe IOTA (or 'range' and 'xrange') are not all that useful ax
examples.

> If something ships with the language then in most cases everyone
> will use that, instead of trying an alternative library.
> Now there are 26 pattern matching libs for CL, and they have an
> accumulated number of 17 users worldwide.

Now, now, now... :) there is only *one* all-encopassing pattern
matching (and more) library which accounts for most of the 17 users:
CL-UNIFICATION (shameless plug).

> CLers like if the language offers already good bit of reusable code.
> Otherwise they may have become Scheme users.

I think this is a non-sequitur :)

> Clojure goes some steps forward. Unlike CL it is not bound to limit
> itself with the functions defined by the HS.
> Those functions are available to all Clojure developers, and there is
> a big agreement to use them. This makes code and libs more compatible
> and much easier to read.

Sounds good to me. I just think that you are not making any good case
with your snippets and your arguments are not much different from
CLers telling Schemers that they should use CL and viceversa.

Cheers
--
Marco

André Thieme

unread,
Feb 14, 2009, 4:09:58 PM2/14/09
to
Slobodan Blazeski schrieb:

> You don't want to hear anything except only the best about Clojure.
> Ok Clojure has some nice things, I agree, but its a frickin' language
> it has flaws too. And recur and stupid tarmplones are one of them.

You are mistaken if you think that I want to only hear what others like
about Clojure. To be able to do that one actually has to be a Clojure
user.
But in c.l.l it’s only the guys who don’t use Clojure who tell me what
is wrong with it, which is kind of amazing (and not in the good sense).
Write 1000-2000 lines of Clojure code, play with it for two month, and
then come back telling me what’s wrong with it.

I agree that Clojure has flaws. Recur is not one of them.
One could say that trampolines are. When I use a JVM with TCO I would
not use trampolines anymore. I could then also stop using recur, but
I won’t.

You guys who defend CL (I am one of those guys also) against attacks
from outsiders should better accept your own arguments, that this time
are used in favour of a different dialect.

I beg to differentiate between warts in Clojure vs those in the JVM.
Just because the GC of the JVM outperforms easily the one of the
commercial Lisp vendors is no weakness of CL itself.
It’s an implementation detail, which can fall back to the language
which is hosted on the VM.
I heared people making the most ridiculous statements about CL, because
it does not have threads or networking libs.
A non-CL user comes here, says „CL sucks, one can’t do multithreading”,
and we shake our heads.
Today:
CL guys come here, never used Clojure, and they say:
„Waah, Clojure sucks badly, it has no recursion. BTW recur is an evil
design error, hahaha!”
Again, head-shaking on the side of the experts, who actually know it
better.

I always agreed that the absence of TCO in todays JVMs is a disad-
vantage. And I also see the trampolines in Clojure as a non-optimal
solution, as it introduces a (very small) extra complexity (bot not
zero), and slows down our programs.
Anyway, a flaw in the JVM that weights more is the behaviour of the
number boxing. CLs already mention this very well, and can outperform
Clojure code that does not use unboxed numbers.
Many people have this on their wishlist for the JVM.

One little wart in Clojure in my opinion are the mechanisms to generate
a class or an interface.
gen-class is a macro that takes up to 12 args (can be compared in a way
with CLs defclass). Of course, it is not Hickeys fault. The oop system
just requires this kind of complexity. But it’s the part in Clojure that
I dislike most.
Fortunately this is not needed very often.


> I'm sure they are when speaking with you.
> Andre Thieme: The ocean is red.
> Unlucky Person: It is usually blue.
> Andre Thieme: No its red.
> Unlucky Person:[Tries to explain that ocean is blue or blue-green]
> Andre Thieme: No its red. Ever heard of red sea?

Haha, that one was actually good! :)
*thumbs up*

Marco Antoniotti

unread,
Feb 14, 2009, 4:19:03 PM2/14/09
to

Only if 'range' is eager. Clojure returns a lazy range.

Cheers
--
Marco

André Thieme

unread,
Feb 14, 2009, 4:29:58 PM2/14/09
to
William James schrieb:
> André Thieme wrote:

>> In Clojure I could do:
>> (apply + (range 12345678)))
>>
>> or use reduce instead of apply.
>
> Wouldn't it be inefficient to create a list or array of size 12345678?

The inefficiency comes from the JVMs boxing behaviour.
range creates a lazy sequence:
user> (class (range 5))
clojure.lang.Range
user> (class [1 2 3 4 5])
clojure.lang.LazilyPersistentVector
user> (class '(1 2 3 4 5))
clojure.lang.PersistentList

(class tells us what type an object has)
range will create a boxed number object, unbox it for use in + and box
the result again.

Slobodan Blazeski

unread,
Feb 14, 2009, 6:45:55 PM2/14/09
to
On Feb 14, 9:53 pm, Raffael Cavallaro

<raffaelcavall...@pas.espam.s.il.vous.plait.mac.com> wrote:
> On 2009-02-14 11:17:13 -0500, André Thieme
> <address.good.until.2009.may...@justmail.de> said:
>
> > Without talking specifically to you Alex, I observed that some CLers try
> > to jump on the recur issue, as this is in their eyes the weakest point.
> > The ironical thing about this is that at leats to my knowledge 100% of
> > those people actually never programmed in Clojure, and experience them-
> > selves how good/bad the situation really is.
>
> I think the situation is a bit more nuanced than that. I for one have
> used clojure and like it very much. But you're trying to make a virtue
> of a necessity with loop/recur. It exists because the jvm doesn't do
> tail call optimization. If the jvm did, then one would simply do
> ordinary recursive calls, and/or clojure would have the equivalent of
> scheme's letrec and named let.
>
> The real innovations of clojure as a lisp dialect are:
>
> 1. the default functional semantics for data structures that are
> traditionally mutable in lisp (conses, lists, hash maps, vectors) but
> which are "persistent," aka, immutable, in clojure, in conjunction
> with...
One question about those immutable structures, by persistent I
understand something that will stay on the disc when someone trips
over the cable. If for example I have some paralelizable computation,
but it has some dog-slow part that thanks to god could be memoized. So
I want to have some central hash table that all the threads could
access for checking if any of the threads already computed that value
before, and also any thread will add new values once computed by it.
So how I'm gonna do that with immutable data structures?
Just for illustration consider calculating fibonacci numbers in
multiple threads with memoization into global hash table for all the
threads.

cheers
bobi

Pascal Costanza

unread,
Feb 14, 2009, 7:09:28 PM2/14/09
to

Most of the time, you want to avoid accesses to shared data structures
when doing parallel computations, for performance reasons.

However, Clojure provides mutable data structures as well, they are just
not the default ones.


Pascal

--
ELS'09: http://www.european-lisp-symposium.org/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/

Aatu Koskensilta

unread,
Feb 14, 2009, 7:23:35 PM2/14/09
to
"Alex Mizrahi" <udod...@users.sourceforge.net> writes:

> AK> What is the Haskell analogue of LABELS?
>
> let .. in or where:

Ah, so LABELS is essentially equivalent to recursive let (for
functions)?

Rob Warnock

unread,
Feb 14, 2009, 8:30:39 PM2/14/09
to
Slobodan Blazeski <slobodan...@gmail.com> wrote:
+---------------
| There was a joke about NASA(or RFSA) spending a lot of money for pens
| that could write in zero gravity. When they announced their
| masterpiece reporters asked them why they don't use pencils?
+---------------

Closer to libel than a joke, actually:

http://en.wikipedia.org/wiki/Space_Pen
http://en.wikipedia.org/wiki/Writing_in_space

The Fisher Space Pen (and others similar to it) was developed with
private funds:

There exists a common urban legend claiming that the Americans spent
$11 million developing the Space Pen, and the Russians used a pencil.
In fact, NASA programs have used pencils (for example a 1965 order
of mechanical pencils) but because of the danger that a broken-off
pencil tip poses in zero gravity and the flammable nature of the wood
present in pencils a better solution was needed.

NASA never approached Paul Fisher to develop a pen, nor did Fisher
receive any government funding for the pen's development. Fisher
invented it independently, and then asked NASA to try it. After the
introduction of the AG7 Space Pen, both the American and Soviet
(later Russian) space agencies adopted it. Previously both the
Russian and American astronauts used grease pencils and plastic slates.


-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

Rob Warnock

unread,
Feb 14, 2009, 8:59:35 PM2/14/09
to
André Thieme <address.good.un...@justmail.de> wrote:
+---------------

| Marco Antoniotti schrieb:
| >> In Clojure I would probably do:
| >> (defn factorial [n] (apply * (range 1 (inc n))))
| >
| > As in
| > (defun factorial (n) (apply '* (iota 1 n)))
| > It isn't very compelling...
...

| The iota function has, no idea, 92 users in the world.
| Probably 7 who use it regularily.
| (I don’t mean these numbers literally, but I have not seen that anyone
| used those, neither in open source, nor in my professional work).
+---------------

Then you must not have been reading "comp.lang.lisp" very long. ;-}
A quick search shows some 170 articles mentioning it, including
several by me: ;-} ;-}

Newsgroups: comp.lang.lisp
Date: Sat, 18 Oct 2008 04:50:42 -0500
Subject: Re: Lisp 50 event at OOPSLA - worth going?
From: rp...@rpw3.org (Rob Warnock)
Message-ID: <96ydnYMohZjvLGTV...@speakeasy.net>

... I even baked a cake!! ;-} ;-}

> (format t "~%+~62,,,'-@a~%|~18@t*~36r ~36r, ~36r!!*~19@t|~%~
| ~{~<|~%| ~1,70:;Candle #~d~>~^, ~
~:*~[ ~; ~; ~; ~; ~; ~; ~; ~; ~; ~]~} |~%+~62,,,'-@a~%~%"
#\+ 29053366 902869993114 1004137 (iota 50 1) #\+)


Newsgroups: comp.lang.lisp
Date: Mon, 26 May 2008 04:23:15 -0500
Subject: Re: Separate the lisp environment from the shell (and other things)
From: rp...@rpw3.org (Rob Warnock)
Message-ID: <PImdnZ2fg50eHKfV...@speakeasy.net>

Rares Marian <rares....@gmail.com> wrote:
+---------------
| We should be at a point where we can start lisp programs
| from the shell or desktop just like any other binary.
+---------------

Been there, done that, years & years ago. Works just fine:

$ iota 10
0 1 2 3 4 5 6 7 8 9
$ iota 10 20
20 21 22 23 24 25 26 27 28 29
$ iota 10 20 5
20 25 30 35 40 45 50 55 60 65
$ cat `which iota`
#!/usr/local/bin/cmucl -script

(defun iota (count &optional (start 0) (step 1))
(loop repeat count for i from start by step collect i))

(format t "~{~a~^ ~}~%"
(apply 'iota (mapcar #'read-from-string *script-args*)))
$
...
p.s. See <http://rpw3.org/hacks/lisp/site-switch-script.lisp>
if you use CMUCL and haven't hacked your own "-script" or equiv.

Then there was a whole thread in late 2007 on ARRAY-IOTA.

And we haven't even started in on the Scheme users, yet... ;-}
[They like it over there, too!]

Anyway, to the larger point...

For me, the issue isn't so much whether Implementation X provides a
given CL extension or not, it's whether the meme is sufficiently stable
and widespread that one can use it with some confidence that you will
be understood by others without lengthy explanations. IOTA has *long*
ago passed that threshold [though there is still some variations in
the wild, e.g., the order of the optional START & STEP arguments].
And DEFLEX (or DEFLEXICAL) is getting there too, I think...

Message has been deleted

André Thieme

unread,
Feb 14, 2009, 9:48:44 PM2/14/09
to
Slobodan Blazeski schrieb:

The persistant datastructures in Clojure are immutable. It will not make
sure that they get written to disk.

About your example:
(def x {}) ==> x evaluates to the empty hashmap, and that can not be
changed.

If we want to introduce your shared memoization state, then we say:
(def *db* (atom {}))

Now *db* is an atom to an empty hashmap.
atoms can change, in a concurrency-safe way.

To add a result of a fib call to the db each thread will do:
(swap! *db* assoc n x)
where n is the arg to fib and result the x the result value.

This would safely add the key n (argument to fibonacci) with the
value result into the *db*.
Clojures STM makes sure that the db will stay consistent.
Here a fast hack:
(defn fib [n]
(when-not (get @*db* n)
(swap! *db* assoc n
(loop [i n, f1 0, f2 1]
(cond (zero? i) f1
(= i 1) f2
:else (recur (dec i) f2 (+ f1 f2))))))
(get @*db* n))

The function always returns what the get at the end returns.
If the value does not already exist it get’s calculated in the
loop and then put into the *db*.
It’s not production ready code, but instead something that can
illustrate the idea.

André Thieme

unread,
Feb 14, 2009, 9:54:26 PM2/14/09
to
Rob Warnock schrieb:

> André Thieme <address.good.un...@justmail.de> wrote:
> +---------------
> | Marco Antoniotti schrieb:
> | >> In Clojure I would probably do:
> | >> (defn factorial [n] (apply * (range 1 (inc n))))
> | >
> | > As in
> | > (defun factorial (n) (apply '* (iota 1 n)))
> | > It isn't very compelling...
> ...
> | The iota function has, no idea, 92 users in the world.
> | Probably 7 who use it regularily.
> | (I don't mean these numbers literally, but I have not seen that anyone
> | used those, neither in open source, nor in my professional work).
> +---------------
>
> Then you must not have been reading "comp.lang.lisp" very long. ;-}
> A quick search shows some 170 articles mentioning it, including
> several by me: ;-} ;-}

Oh oki, thanks for correcting me!
I am reading c.l.l since early 2003, but it seems I missed those.


André
--

Scott

unread,
Feb 14, 2009, 10:01:05 PM2/14/09
to
On Feb 14, 6:59 pm, r...@rpw3.org (Rob Warnock) wrote:
>
>     > (format t "~%+~62,,,'-@a~%|~18@t*~36r ~36r, ~36r!!*~19@t|~%~
>                  | ~{~<|~%| ~1,70:;Candle #~d~>~^, ~
>                  ~:*~[ ~; ~; ~; ~; ~; ~; ~; ~; ~; ~]~}  |~%+~62,,,'-@a~%~%"
>                  #\+ 29053366 902869993114 1004137 (iota 50 1) #\+)
>

Is that APL?


j/k

Message has been deleted

André Thieme

unread,
Feb 14, 2009, 10:16:23 PM2/14/09
to
Madhu schrieb:
> * André Thieme <gn7vqj$2as$1...@news.motzarella.org> :
> Wrote on Sun, 15 Feb 2009 03:48:44 +0100:
>
> | Slobodan Blazeski schrieb:

> |> One question about those immutable structures, by persistent I
> |> understand something that will stay on the disc when someone trips
> |> over the cable. If for example I have some paralelizable computation,
> |> but it has some dog-slow part that thanks to god could be memoized. So
> |> I want to have some central hash table that all the threads could
> |> access for checking if any of the threads already computed that value
> |> before, and also any thread will add new values once computed by it.
> |> So how I'm gonna do that with immutable data structures?
> |> Just for illustration consider calculating fibonacci numbers in
> |> multiple threads with memoization into global hash table for all the
> |> threads.
>
> <snip>

>
> | The function always returns what the get at the end returns.
> | If the value does not already exist it get’s calculated in the
>
> But this misses out your best marketing feature for the language
>
> <sarcasm>
>
> In the time you spend waiting for the lock to the shared table to do a
> table lookup to compute a value, you can use a "free core" to do the
> computation from scratch just as quickly.
>
> STM with multi core technology really shines on problem with solutions
> that can be expressed as simple table-lookup. --- in keeping all cores
> occupied at 100% CPU
>
> </sarcasm>

Slobodan mentioned explicitly the fibonacci function as an example.
He wants it for more expensive calculations I suppose.
And btw, in general there is no need to wait for locks, as Clojure is
semi-optimistic. It begins writing to the datastructure in a reversible
and yet invisible way (while still in the transaction), and if it finds
out that the datastructure was modified by another thread, it will run
the transaction again. If no other thread wrote data, then a lock is
used under the hood for an extremly short amount of time, to let the
already written data go live.
It’s true that in my example this is not the case, as I don’t buffer
the result of fib in a let first and then do a swap!.
And please be fair: I wrote that this was a fast hack. I invite you to
rewrite it into a high performance version.

Rob Warnock

unread,
Feb 15, 2009, 1:03:31 AM2/15/09
to
Madhu <eno...@meer.net> wrote:
+---------------
| * (Rob Warnock) <k5GdnQM7O_aa6grU...@speakeasy.net> :

| | Then there was a whole thread in late 2007 on ARRAY-IOTA.
|
| Which reminds me to ask: could you consider and releasing OPFR?
+---------------

Sure, no problem. I'll put that on the "to do real soon" list,
and will post here when that happens.

+---------------
| (From the descriptions i think It could serve as a reference for other
| efforts along the lines)
+---------------

Perhaps. ;-} Even though I've been inflicting it on innocent live
users for over a decade(!), I'm still not completely decided on how it
really "ought" to work, since that seems to vary considerably depending
on what application and user group it's being used with. E.g., I've
flip-flopped back & forth several times on how to handle exceptions:
Show an (abbreviated) backtrace. or just PRINC the condition? Drop into
the debuuger or just bounce back to the top-level prompt? "It depends",
as they say. For these reasons (and others), it really should be
considered as merely one instance of a general pattern, rather than
any kind of cast-in-stone "library" or "module" that one can just
grab off the shelf and use blindly. [You have been warned... ;-} ]

Rob Warnock

unread,
Feb 15, 2009, 1:10:06 AM2/15/09
to
Scott <xsc...@gmail.com> wrote:
+---------------
+---------------

I think it's called FORMAT pr0n! ;-} ;-}

William James

unread,
Feb 15, 2009, 3:02:56 AM2/15/09
to
Rob Warnock wrote:

> ... I even baked a cake!! ;-} ;-}
>
> > (format t "~%+~62,,,'-@a~%|~18@t*~36r ~36r, ~36r!!*~19@t|~%~
> | ~{~<|~%| ~1,70:;Candle #~d~>~^, ~
> ~:*~[ ~; ~; ~; ~; ~; ~; ~; ~; ~; ~]~}
> |~%+~62,,,'-@a~%~%" #\+ 29053366 902869993114
> 1004137 (iota 50 1) #\+)

Ruby:

c=proc{|s|puts"|#{s.center(61)}|"};puts s="+#{'-'*61}+"
c["*%s %s, %s!!*"%[25970909,742283400809,1106826].map{|n|n.to_s 35}]
1.step(50,5){|i|c[(i..i+4).map{|n|"Candle #%-3s"%"#{n},"}.join" "]}
puts s

+-------------------------------------------------------------+
| *happy birthday, psil!!* |
| Candle #1, Candle #2, Candle #3, Candle #4, Candle #5, |
| Candle #6, Candle #7, Candle #8, Candle #9, Candle #10, |
| Candle #11, Candle #12, Candle #13, Candle #14, Candle #15, |
| Candle #16, Candle #17, Candle #18, Candle #19, Candle #20, |
| Candle #21, Candle #22, Candle #23, Candle #24, Candle #25, |
| Candle #26, Candle #27, Candle #28, Candle #29, Candle #30, |
| Candle #31, Candle #32, Candle #33, Candle #34, Candle #35, |
| Candle #36, Candle #37, Candle #38, Candle #39, Candle #40, |
| Candle #41, Candle #42, Candle #43, Candle #44, Candle #45, |
| Candle #46, Candle #47, Candle #48, Candle #49, Candle #50, |
+-------------------------------------------------------------+

William James

unread,
Feb 15, 2009, 3:36:55 AM2/15/09
to
William James wrote:

One too many commas.


c=proc{|s|puts"|#{s.sub(/5.,/,"50").center 61}|"};puts s="+#{'-'*61}+"


c["*%s %s, %s!!*"%[25970909,742283400809,1106826].map{|n|n.to_s 35}]
1.step(50,5){|i|c[(i..i+4).map{|n|"Candle #%-3s"%"#{n},"}.join" "]}
puts s

+-------------------------------------------------------------+
| *happy birthday, psil!!* |
| Candle #1, Candle #2, Candle #3, Candle #4, Candle #5, |
| Candle #6, Candle #7, Candle #8, Candle #9, Candle #10, |
| Candle #11, Candle #12, Candle #13, Candle #14, Candle #15, |
| Candle #16, Candle #17, Candle #18, Candle #19, Candle #20, |
| Candle #21, Candle #22, Candle #23, Candle #24, Candle #25, |
| Candle #26, Candle #27, Candle #28, Candle #29, Candle #30, |
| Candle #31, Candle #32, Candle #33, Candle #34, Candle #35, |
| Candle #36, Candle #37, Candle #38, Candle #39, Candle #40, |
| Candle #41, Candle #42, Candle #43, Candle #44, Candle #45, |
| Candle #46, Candle #47, Candle #48, Candle #49, Candle #50 |

+-------------------------------------------------------------+

Marco Antoniotti

unread,
Feb 15, 2009, 4:06:29 AM2/15/09
to
On Feb 15, 3:39 am, Madhu <enom...@meer.net> wrote:
> * (Rob Warnock) <k5GdnQM7O_aa6grUnZ2dnUVZ_j2dn...@speakeasy.net> :
> Wrote on Sat, 14 Feb 2009 19:59:35 -0600:

>
> | Then there was a whole thread in late 2007 on ARRAY-IOTA.
>
> Which reminds me to ask: could you consider and releasing OPFR?

Wow. I *am* getting old. What is a OPFR?

Cheers
--
Marco

Pascal Costanza

unread,
Feb 15, 2009, 4:34:08 AM2/15/09
to
Aatu Koskensilta wrote:
> "Alex Mizrahi" <udod...@users.sourceforge.net> writes:
>
>> AK> What is the Haskell analogue of LABELS?
>>
>> let .. in or where:
>
> Ah, so LABELS is essentially equivalent to recursive let (for
> functions)?

Yes. LABELS was actually also the original name for letrec in Scheme -
they only switched from labels to letrec at some later stage.

LABELS stems from the original LABEL form in Lisp 1.5, which allowed you
to write one (!) function that can call itself.

Rob Warnock

unread,
Feb 15, 2009, 5:21:49 AM2/15/09
to
Marco Antoniotti <mar...@gmail.com> wrote:
+---------------
| Madhu <enom...@meer.net> wrote:

| > (Rob Warnock) <k5GdnQM7O_aa6grUnZ2dnUVZ_j2dn...@speakeasy.net> wrote:
| > | Then there was a whole thread in late 2007 on ARRAY-IOTA.
| >
| > Which reminds me to ask: could you consider and releasing OPFR?
|
| Wow. I *am* getting old. What is a OPFR?
+---------------

"OPFR" == "Outer-Parenthesis-Free REPL". It's a meme/pattern/library(?)
that I've been talking about & using since early 1995 [if not earlier!],
first in Scheme, then in Common Lisp. The idea is simplicity itself:
a command-line reader that wrap a set of parens around whatever the user
types and then passes that to EVAL [with some minor tweaks so the most
common cases "do the right thing"]. The "rationale" comment from the
current CL version [soon to be public]:

;;; OPFR provides a simple command-line read-eval-print loop (REPL) for
;;; programs written in Common Lisp where the primary user community
;;; is uncomfortable using normal Lisp symbolic expressions (sexps).
;;; The simplicity and utility of OPFR derives from the observation
;;; that most such people are actually surprisingly accepting of an
;;; sexp-based interface *provided* that they are not required
;;; to manually type the outer pair of parentheses, *even if* any
;;; sub-expressions are still in pure Lisp sexp form!! This effect
;;; is even stronger when the majority of the command functions in the
;;; using application are provided as functions or macros, which only
;;; seldom require the typing of sub-expressions.
;;;
;;; This short example compares the syntaxes. First, normal Common Lisp:
;;; > (+ 1 2)
;;; 3
;;; > (defvar x 34)
;;; X
;;; > (defvar y 25)
;;; Y
;;; > (expt x y)
;;; 193630125104980427932766033374162714624
;;; > (expt x (- y 12))
;;; 81138303245565435904
;;; >
;;; Now, exactly the same sequence of operations using OPFR syntax:
;;; opfr> + 1 2
;;; ==> 3
;;; opfr> defvar x 34
;;; ==> X
;;; opfr> defvar y 25
;;; ==> Y
;;; opfr> expt x y
;;; ==> 193630125104980427932766033374162714624
;;; opfr> expt x (- y 12)
;;; ==> 81138303245565435904
;;; opfr>
;;;
;;; Note: The average Lisp programmer will see no significant advantage to
;;; the OPFR syntax (and some disadvantages, such as the need to resolve
;;; the ambiguity of a naked symbol -- should OPFR print its value as
;;; a global variable or call it as a "command" function?), especially
;;; since sub-expressions must still be fully-parenthesized (as in the
;;; last example above). Nevertheless, experience with real users has
;;; shown that the acceptance of the OPFR syntax is *enormously* greater
;;; than the "pure" Lisp sexp. [Go figure... (*sigh*)]

Search for me & OPFR or me & HWTOOL and you'll see lots more examples, e.g.:

Subject: Re: Why is LISP syntax superior?
Message-ID: <c6CdndrAWrIcbgDZ...@speakeasy.net>

Subject: Re: sweet-expressions instead of s-expressions?
Message-ID: <haednUzCwZrQmovY...@speakeasy.net>

Subject: Re: MUSING: standard bodies vs benevolent dictators, popularity
Message-ID: <r7ydnaOXndd...@speakeasy.net>

Marco Antoniotti

unread,
Feb 15, 2009, 6:54:11 AM2/15/09
to
On Feb 15, 11:21 am, r...@rpw3.org (Rob Warnock) wrote:
>     Message-ID: <c6CdndrAWrIcbgDZnZ2dnUVZ_qKdn...@speakeasy.net>

>
>     Subject: Re: sweet-expressions instead of s-expressions?
>     Message-ID: <haednUzCwZrQmovYnZ2dnUVZ_tqdn...@speakeasy.net>

>
>     Subject: Re: MUSING: standard bodies vs benevolent dictators, popularity
>     Message-ID: <r7ydnaOXnddSZjXeRVn...@speakeasy.net>
>

Ok. I use it pretty often in LW. It does exactly that.

Cheers
--
Marco

Slobodan Blazeski

unread,
Feb 15, 2009, 7:15:17 AM2/15/09
to
On Feb 15, 3:48 am, André Thieme <address.good.until.

Cool thanks for the explanation.
bobi

Pascal Costanza

unread,
Feb 15, 2009, 8:14:34 AM2/15/09
to
André Thieme wrote:
> Slobodan Blazeski schrieb:
>> On Feb 14, 9:53 pm, Raffael Cavallaro
>> <raffaelcavall...@pas.espam.s.il.vous.plait.mac.com> wrote:
>>> The real innovations of clojure as a lisp dialect are:
>>>
>>> 1. the default functional semantics for data structures that are
>>> traditionally mutable in lisp (conses, lists, hash maps, vectors) but
>>> which are "persistent," aka, immutable, in clojure, in conjunction
>>> with...
>> One question about those immutable structures, by persistent I
>> understand something that will stay on the disc when someone trips
>> over the cable. If for example I have some paralelizable computation,
>> but it has some dog-slow part that thanks to god could be memoized. So
>> I want to have some central hash table that all the threads could
>> access for checking if any of the threads already computed that value
>> before, and also any thread will add new values once computed by it.
>> So how I'm gonna do that with immutable data structures?
>> Just for illustration consider calculating fibonacci numbers in
>> multiple threads with memoization into global hash table for all the
>> threads.
>
> The persistant datastructures in Clojure are immutable.

There is a library for Common Lisp which does something similar:
http://common-lisp.net/project/fset/

Rob Warnock

unread,
Feb 15, 2009, 8:45:31 AM2/15/09
to
Marco Antoniotti <mar...@gmail.com> wrote:
+---------------
| rp...@rpw3.org (Rob Warnock) wrote:
| > "OPFR" == "Outer-Parenthesis-Free REPL". It's a meme/pattern/library(?)...
| > ...a command-line reader that wrap a set of parens around whatever the

| > user types and then passes that to EVAL [with some minor tweaks so the
| > most common cases "do the right thing"]. ...
...

|
| Ok. I use it pretty often in LW. It does exactly that.
+---------------

Interesting. A couple of questions about the LW version:

- Will it let you continue a line? E.g.,

opfr> + 1 2 \
3 4

10
opfr>

- OPFR tries to guess whether a single symbol on a line is a function
or a value and "do the right thing". This creates an ambiguity if
a symbol has both a functional and variable value. OPFR favors the
functional value, since you can always get the variable value by
prefixing it with VALUES, e.g.:

opfr> get-universal-time

3443692633
opfr> most-positive-fixnum

536870911
opfr> defun foo () "A function value"

FOO
opfr> defvar foo "A variable value"

FOO
opfr> foo

"A function value"
opfr> values foo

"A variable value"
opfr>

How does LW handle that?

- Because of the previous, one can overload naked keywords as "commands",
if one likes. OPFR currently uses only :Q (and :QUIT), but others
could be added easily. Does LW do anything like that?

Marco Antoniotti

unread,
Feb 15, 2009, 9:38:31 AM2/15/09
to
On Feb 15, 2:45 pm, r...@rpw3.org (Rob Warnock) wrote:
> Marco Antoniotti  <marc...@gmail.com> wrote:
> +---------------

It is not as sophisticated as what you propose....

Cheers
--
Marco

Message has been deleted

André Thieme

unread,
Feb 15, 2009, 10:32:34 AM2/15/09
to
Pascal Costanza schrieb:

> André Thieme wrote:
>> Slobodan Blazeski schrieb:
>>> On Feb 14, 9:53 pm, Raffael Cavallaro
>>> <raffaelcavall...@pas.espam.s.il.vous.plait.mac.com> wrote:
>>>> The real innovations of clojure as a lisp dialect are:
>>>>
>>>> 1. the default functional semantics for data structures that are
>>>> traditionally mutable in lisp (conses, lists, hash maps, vectors) but
>>>> which are "persistent," aka, immutable, in clojure, in conjunction
>>>> with...
>>> One question about those immutable structures, by persistent I
>>> understand something that will stay on the disc when someone trips
>>> over the cable. If for example I have some paralelizable computation,
>>> but it has some dog-slow part that thanks to god could be memoized. So
>>> I want to have some central hash table that all the threads could
>>> access for checking if any of the threads already computed that value
>>> before, and also any thread will add new values once computed by it.
>>> So how I'm gonna do that with immutable data structures?
>>> Just for illustration consider calculating fibonacci numbers in
>>> multiple threads with memoization into global hash table for all the
>>> threads.
>>
>> The persistant datastructures in Clojure are immutable.
>
> There is a library for Common Lisp which does something similar:
> http://common-lisp.net/project/fset/

I think all major languages will get a STM with MVCC in the coming
months or years.
That will hopefully help to make further improvements to software in
general.
One problem I personally see though is, that a STM as an addition to
a language is not as good as if it was directly built into a functional
one. If the language offers imperative programming as well, and easier
access to non-persistant datastructures (such as [] in Python or () in
Lisp, ...) more than enough programmers won’t use the STM in the way
they should. Teams will have to agree on things, need meetings, and so
on. In Haskell, Clojure and Erlang the concurrency safety is the default.
But still, I hope to see soon how Franz and Lispworks will ship their
own STM.

Rich Hickey

unread,
Feb 15, 2009, 10:57:44 AM2/15/09
to
On Feb 14, 9:54 am, Raffael Cavallaro
<raffaelcavall...@pas.espam.s.il.vous.plait.mac.com> wrote:
> On 2009-02-14 07:15:10 -0500, André Thieme
> <address.good.until.2009.may...@justmail.de> said:
>
> > First of all, I would like to mention that recur also has no disadvantages.
>
> It can't be used for mutual recursion. Common Lisp's labels can;
> scheme's letrec can. Clojure needs a separate piece of
> work-around-the-jvm syntax for mutual recursion.
>

I'm loathe to jump into this discussion, as I see no point in 'Clojure
vs CL', but I think a few points need clarification:

Clojure of course supports recursion, it just doesn't guarantee that
it won't consume stack - same as CL. loop/recur is not a substitute
for TCO. It is just Clojure's analogue to CL's 'do', an iteration
construct that happens to look like a recursive call because I prefer
that style to that of CL's do - but it's nothing more than a simple
loop. It is workaround syntax only to the extent that CL's do is
workaround syntax - neither language guarantees TCO so both must have
iteration constructs. It is only put forth as an option to those
coming from Scheme looking for TCO in the self-call case, and always
with the caveat that it does not support mutual recursion.

Clojure has nothing against iteration, there is also dotimes and
doseq, analogues to CL's dotimes and dolist.

The absence of letrec/labels in Clojure is completely independent - I
have nothing against it and just haven't gotten around to it yet. When
I do, such mutually recursive calls may consume stack, as with CL's
labels. The absence of letrec/labels only precludes mutually recursive
local functions - mutually recursive global functions are already
supported, as are normally self-recursive local functions without
using recur:

(fn aname [arg] ... (aname 42))

I hope this clarifies things, and sorry to see the arguing,

Rich


Pascal Costanza

unread,
Feb 15, 2009, 11:13:57 AM2/15/09
to

That's not so clear yet. See "Software Transactional Memory: why is it
only a research toy?" at http://doi.acm.org/10.1145/1400214.1400228

> That will hopefully help to make further improvements to software in
> general.
> One problem I personally see though is, that a STM as an addition to
> a language is not as good as if it was directly built into a functional
> one. If the language offers imperative programming as well, and easier
> access to non-persistant datastructures (such as [] in Python or () in
> Lisp, ...) more than enough programmers won’t use the STM in the way
> they should. Teams will have to agree on things, need meetings, and so
> on. In Haskell, Clojure and Erlang the concurrency safety is the default.
> But still, I hope to see soon how Franz and Lispworks will ship their
> own STM.

STM is definitely an interesting approach and could turn out as a good
abstraction to support parallel programming (although it's definitely
not sufficient, not only because of its current shortcomings - see
above). However, it's not the only one and there are other interesting
high-level abstractions for parallel programming.

Currently, the typical support in CL implementations is to provide
threads + locking + some other forms of synchronization. It seems to me
that these are the 'right' low-level facilities to build different
high-level facilities on top.

Traditionally, the Lisp approach is not to provide one high-level
solution for a particular programming problem, but to provide several
different low-level means and give programmers the expressive power to
build their own high-level abstractions on top. That's why Lisp stresses
metaprogramming and reflection (in the form of macros or MOPs, for
example), because this gives you the way to reach underneath your
current abstraction layer and influence the decisions there. And that's
also why I currently think that threads + locking + means for
synchronization may actually be the best Lisp dialects can offer.

Lisp doesn't force you to program in a functional style, but you can
also program in an imperative style, in an object-oriented style, in a
logic style, etc. The same should be true for parallel programming,
IMHO: You shouldn't only be able to use actor-style, but also data
parallelism, and different kinds of synchronization mechanisms.
Otherwise you may lose too much depending on the particular kind of
problem you want to solve.

Clojure is a very interesting project, and it's definitely interesting
to have persistent datatypes at the core of the language. However, my
main gripes with Clojure are the following:

- It promotes purely functional programming. That's sometimes not
appropriate.

- It offers only STM for stateful programming. That's also sometimes not
appropriate.

- AFAICT, the only way to reflectively extend the language is by going
down to the Java level. That shouldn't be necessary. A good Lisp dialect
should allow you to stay within Lisp even if you want to influence the
implementation of the language itself.

I smell the following two typical problems for the kind of languages
that Clojure is an example of:

- It is advertized as making programming a lot simpler than before, in
this case as making parallel programming a lot simpler than before.
However, all languages that started out as simple languages in the
beginning turned out to become much more complex in the long run, and
especially suffered from the initial simplifying assumptions.

- It makes a distinction between what the designer of the language is
allowed to do internally in its implementation, and what 'users' of the
language are allowed to do. This removes expressive power from the users
/ programmers, which will eventually hurt them in the long run.

These are the main two reasons why I am skeptical of Clojure, in spite
of it being one of the more interesting recent developments in Lisp.

Clojure is probably very useful in some scenarios, but it's most likely
not the final answer to everything. ;)

Raffael Cavallaro

unread,
Feb 15, 2009, 1:54:15 PM2/15/09
to
On 2009-02-15 10:57:44 -0500, Rich Hickey <richh...@gmail.com> said:

> I'm loathe to jump into this discussion, as I see no point in 'Clojure
> vs CL', but I think a few points need clarification:

First, thanks for the clarifications, and especially thanks for
clojure. It's a great language and I'm really happy to see and use your
innovations in the lisp language design space.

>
> Clojure of course supports recursion, it just doesn't guarantee that
> it won't consume stack - same as CL.

You are of course right when you say that clojure has exactly the same
capabilities wrt TCO as the common lisp standard. The thing is, it is
common[1] for lisp implementations to go beyond the standard in this
regard (ccl, sbcl, 64bit lispworks, allegro cl), and so, beyond what
clojure does.

> loop/recur is not a substitute
> for TCO. It is just Clojure's analogue to CL's 'do', an iteration
> construct that happens to look like a recursive call because I prefer
> that style to that of CL's do - but it's nothing more than a simple
> loop.

With respect, I think we're just doing semantics here. To me, TCO is
the process of taking tail recursive syntax and turning it into
iteration. If loop/recur is tail recursive syntax (it is) that is
transformed by the compiler into iteration (a simple loop) then it is a
kind of TCO.

In any event, I will repeat what I wrote to Andre. The real innovations
of clojure are the persistent (i.e., immutable) data structures, the
egal equality semantics, the stm for mutable data, and the absence of
explicit locks in user code, all of which together are greater than the
sum of the parts. Clojure is a lisp which makes doing concurrency
correctly a central part of the semantics of the language. This is,
imho, what sets clojure apart, not loop/recur.

Again, thanks for clojure; I wish you much success with it. And sorry
for the bickering.

regards,

Ralph


[1] bad pun, I know :(

--
Raffael Cavallaro, Ph.D.

Rich Hickey

unread,
Feb 15, 2009, 2:45:20 PM2/15/09
to

That is a weak article. It criticizes STM in comparison to its
performance vs. serial execution. It doesn't compare the STM code with
equivalent versions using manual locking, neither for complexity nor
for performance. Nor does it place appropriate value on correctness.
People were (are!) skeptical of GC too, and there were (are) certainly
places where its performance rules it out. That doesn't negate its
value for people for whom its performance is sufficient and its
correctness lets them focus on their application domain.

http://queue.acm.org/detail.cfm?id=1454464

Articles that essentially state "it's not a panacea" are not that
illuminating. Also, approaches to STM vary quite widely in both their
objectives and implementations. A lot of STM research is directed at
providing STM that allows people to continue working much as they do
now - pretending they are the only process banging on bits of memory
in the machine. I disagree with that premise, and Clojure's STM is
different as a result.

> STM is definitely an interesting approach and could turn out as a good
> abstraction to support parallel programming (although it's definitely
> not sufficient, not only because of its current shortcomings - see
> above). However, it's not the only one and there are other interesting
> high-level abstractions for parallel programming.
>
> Currently, the typical support in CL implementations is to provide
> threads + locking + some other forms of synchronization. It seems to me
> that these are the 'right' low-level facilities to build different
> high-level facilities on top.
>

I think it is tricky to get a good memory model defined that deals
with all of the issues. Java tries:

http://java.sun.com/docs/books/jls/third_edition/html/memory.html

See also:

http://jcip.net/

It's certainly much more subtle and difficult than just "threads +
locking".

> Traditionally, the Lisp approach is not to provide one high-level
> solution for a particular programming problem, but to provide several
> different low-level means and give programmers the expressive power to
> build their own high-level abstractions on top. That's why Lisp stresses
> metaprogramming and reflection (in the form of macros or MOPs, for
> example), because this gives you the way to reach underneath your
> current abstraction layer and influence the decisions there. And that's
> also why I currently think that threads + locking + means for
> synchronization may actually be the best Lisp dialects can offer.
>

Providing low-level constructs doesn't obviate the need for higher-
level ones as well. A box of parts is not a car.

> Lisp doesn't force you to program in a functional style, but you can
> also program in an imperative style, in an object-oriented style, in a
> logic style, etc. The same should be true for parallel programming,
> IMHO: You shouldn't only be able to use actor-style, but also data
> parallelism, and different kinds of synchronization mechanisms.
> Otherwise you may lose too much depending on the particular kind of
> problem you want to solve.
>

Clojure forces very little. It's not Haskell with parens after all.
All those styles are possible, and higher-level support for some comes
included.

> Clojure is a very interesting project, and it's definitely interesting
> to have persistent datatypes at the core of the language. However, my
> main gripes with Clojure are the following:
>
> - It promotes purely functional programming. That's sometimes not
> appropriate.
>

I've never promoted Clojure as "purely functional programming", as I
am quite suspect of the concept implied by the words, and the term has
come to mean "having a type system that keeps the purely functional
parts separate". The presence of the reference types in Clojure makes
it clear that I don't think a purely functional style will get you all
the way there. I also know that the typical approach to state doesn't
either, especially when concurrency comes into play. But Clojure is
definitely an impure functional language. Promoting the use of
immutable data and pure functions is not the same as limiting you to
them.

> - It offers only STM for stateful programming.

This is not true. Clojure offers no fewer than 4 managed reference
types:

STM-based refs
Actor-like asynchronous agents
Thread-local vars
CAS-like atoms

All of which have concurrency semantics. In addition you have access
to locks and all other JVM concurrency primitives.

When I give talks I emphasize the unique parts of Clojure because they
are interesting and that's where Clojure adds value, but that doesn't
mean they are the only parts. You can bash bits in Clojure just like
in CL and Java, there are mutable arrays etc, but what's interesting
about that?

> - AFAICT, the only way to reflectively extend the language is by going
> down to the Java level. That shouldn't be necessary. A good Lisp dialect
> should allow you to stay within Lisp even if you want to influence the
> implementation of the language itself.
>

This is not true. The extensibility points that are defined in terms
of Java interfaces can be extended in Clojure or Java.

> I smell the following two typical problems for the kind of languages
> that Clojure is an example of:
>
> - It is advertized as making programming a lot simpler than before, in
> this case as making parallel programming a lot simpler than before.
> However, all languages that started out as simple languages in the
> beginning turned out to become much more complex in the long run, and
> especially suffered from the initial simplifying assumptions.
>

I usually try not to say Clojure is anything-er than anything else. I
just make it, and say what it does. But don't all Lisps start out as
simple languages, and isn't that the point of Lisp?

> - It makes a distinction between what the designer of the language is
> allowed to do internally in its implementation, and what 'users' of the
> language are allowed to do. This removes expressive power from the users
> / programmers, which will eventually hurt them in the long run.
>

This is a chimera. Why couldn't I just make the cons and sequence
functions of Common Lisp extensible to user-defined types? Then I
wouldn't have had to write Clojure. There are always some things that
are built-in and some that are tweakable. At least Clojure's core
library functions are defined in a way that they can be extended to
other types.

> These are the main two reasons why I am skeptical of Clojure, in spite
> of it being one of the more interesting recent developments in Lisp.
>
> Clojure is probably very useful in some scenarios, but it's most likely
> not the final answer to everything. ;)
>

Clojure's not trying to be a panacea. It's just a tool.

Rich

Pascal Costanza

unread,
Feb 15, 2009, 4:38:42 PM2/15/09
to
Rich Hickey wrote:
> On Feb 15, 11:13 am, Pascal Costanza <p...@p-cos.net> wrote:
>> André Thieme wrote:
>>> I think all major languages will get a STM with MVCC in the coming
>>> months or years.
>> That's not so clear yet. See "Software Transactional Memory: why is it
>> only a research toy?" athttp://doi.acm.org/10.1145/1400214.1400228
>
> That is a weak article. It criticizes STM in comparison to its
> performance vs. serial execution. It doesn't compare the STM code with
> equivalent versions using manual locking, neither for complexity nor
> for performance. Nor does it place appropriate value on correctness.
> People were (are!) skeptical of GC too, and there were (are) certainly
> places where its performance rules it out. That doesn't negate its
> value for people for whom its performance is sufficient and its
> correctness lets them focus on their application domain.

The article has some credibility because the authors have been doing
research on STM themselves.

I agree that is probably a good solution in the long run, but that's not
clear yet and remains to be seen.

> http://queue.acm.org/detail.cfm?id=1454464
>
> Articles that essentially state "it's not a panacea" are not that
> illuminating. Also, approaches to STM vary quite widely in both their
> objectives and implementations. A lot of STM research is directed at
> providing STM that allows people to continue working much as they do
> now - pretending they are the only process banging on bits of memory
> in the machine. I disagree with that premise, and Clojure's STM is
> different as a result.

Yes, it seems hard to avoid the necessity to change programming style
when dealing with parallelism.

>> STM is definitely an interesting approach and could turn out as a good
>> abstraction to support parallel programming (although it's definitely
>> not sufficient, not only because of its current shortcomings - see
>> above). However, it's not the only one and there are other interesting
>> high-level abstractions for parallel programming.
>>
>> Currently, the typical support in CL implementations is to provide
>> threads + locking + some other forms of synchronization. It seems to me
>> that these are the 'right' low-level facilities to build different
>> high-level facilities on top.
>
> I think it is tricky to get a good memory model defined that deals
> with all of the issues. Java tries:
>
> http://java.sun.com/docs/books/jls/third_edition/html/memory.html
>
> See also:
>
> http://jcip.net/
>
> It's certainly much more subtle and difficult than just "threads +
> locking".

You're right, and I'm sorry if I gave the wrong impression here. (I used
"threads + locking" as general placeholder for the bunch of operations
typically provided in multithreaded CL implementations.)

>> Traditionally, the Lisp approach is not to provide one high-level
>> solution for a particular programming problem, but to provide several
>> different low-level means and give programmers the expressive power to
>> build their own high-level abstractions on top. That's why Lisp stresses
>> metaprogramming and reflection (in the form of macros or MOPs, for
>> example), because this gives you the way to reach underneath your
>> current abstraction layer and influence the decisions there. And that's
>> also why I currently think that threads + locking + means for
>> synchronization may actually be the best Lisp dialects can offer.
>
> Providing low-level constructs doesn't obviate the need for higher-
> level ones as well. A box of parts is not a car.

I'm not sure how to comment on this.

>> Lisp doesn't force you to program in a functional style, but you can
>> also program in an imperative style, in an object-oriented style, in a
>> logic style, etc. The same should be true for parallel programming,
>> IMHO: You shouldn't only be able to use actor-style, but also data
>> parallelism, and different kinds of synchronization mechanisms.
>> Otherwise you may lose too much depending on the particular kind of
>> problem you want to solve.
>
> Clojure forces very little. It's not Haskell with parens after all.
> All those styles are possible, and higher-level support for some comes
> included.

OK.

>> Clojure is a very interesting project, and it's definitely interesting
>> to have persistent datatypes at the core of the language. However, my
>> main gripes with Clojure are the following:
>>
>> - It promotes purely functional programming. That's sometimes not
>> appropriate.
>
> I've never promoted Clojure as "purely functional programming", as I
> am quite suspect of the concept implied by the words, and the term has
> come to mean "having a type system that keeps the purely functional
> parts separate". The presence of the reference types in Clojure makes
> it clear that I don't think a purely functional style will get you all
> the way there. I also know that the typical approach to state doesn't
> either, especially when concurrency comes into play. But Clojure is
> definitely an impure functional language. Promoting the use of
> immutable data and pure functions is not the same as limiting you to
> them.

That's why I said "promote," and not "enforce." (I don't see static
types as a necessary ingredient of purely functional programming, only
the absence of effects.)

>> - It offers only STM for stateful programming.
>
> This is not true. Clojure offers no fewer than 4 managed reference
> types:
>
> STM-based refs
> Actor-like asynchronous agents
> Thread-local vars
> CAS-like atoms
>
> All of which have concurrency semantics. In addition you have access
> to locks and all other JVM concurrency primitives.

OK, thanks for the correction. I'm sorry I misrepresented Clojure in
this regard.

> When I give talks I emphasize the unique parts of Clojure because they
> are interesting and that's where Clojure adds value, but that doesn't
> mean they are the only parts. You can bash bits in Clojure just like
> in CL and Java, there are mutable arrays etc, but what's interesting
> about that?

So how do you deal with concurrency when mutating arrays?

>> - AFAICT, the only way to reflectively extend the language is by going
>> down to the Java level. That shouldn't be necessary. A good Lisp dialect
>> should allow you to stay within Lisp even if you want to influence the
>> implementation of the language itself.
>
> This is not true. The extensibility points that are defined in terms
> of Java interfaces can be extended in Clojure or Java.

OK, maybe I'm wrong here. So: Could you, in principle, implement the
four reference types in Clojure itself in terms of lower level
constructs provided by Clojure?

>> I smell the following two typical problems for the kind of languages
>> that Clojure is an example of:
>>
>> - It is advertized as making programming a lot simpler than before, in
>> this case as making parallel programming a lot simpler than before.
>> However, all languages that started out as simple languages in the
>> beginning turned out to become much more complex in the long run, and
>> especially suffered from the initial simplifying assumptions.
>
> I usually try not to say Clojure is anything-er than anything else. I
> just make it, and say what it does. But don't all Lisps start out as
> simple languages, and isn't that the point of Lisp?

No, I don't think that's the point of Lisp.

>> - It makes a distinction between what the designer of the language is
>> allowed to do internally in its implementation, and what 'users' of the
>> language are allowed to do. This removes expressive power from the users
>> / programmers, which will eventually hurt them in the long run.
>
> This is a chimera. Why couldn't I just make the cons and sequence
> functions of Common Lisp extensible to user-defined types? Then I
> wouldn't have had to write Clojure. There are always some things that
> are built-in and some that are tweakable.

True, and certainly both Common Lisp and Scheme lack some important
possibilities to tweak them as well.

> At least Clojure's core
> library functions are defined in a way that they can be extended to
> other types.

Well, I'm wondering about the ability to implement your own abstractions
for parallel programming beyond the default ones provided by Clojure,
within Clojure itself. If that's possible, I have to correct my
statements about Clojure.

>> These are the main two reasons why I am skeptical of Clojure, in spite
>> of it being one of the more interesting recent developments in Lisp.
>>
>> Clojure is probably very useful in some scenarios, but it's most likely
>> not the final answer to everything. ;)
>
> Clojure's not trying to be a panacea. It's just a tool.

Hm, it seems I have described Clojure too negatively. Sorry if that's
the case.

Pascal J. Bourguignon

unread,
Feb 15, 2009, 5:08:29 PM2/15/09
to
Pascal Costanza <p...@p-cos.net> writes:
>>> Traditionally, the Lisp approach is not to provide one high-level
>>> solution for a particular programming problem, but to provide several
>>> different low-level means and give programmers the expressive power to
>>> build their own high-level abstractions on top. That's why Lisp stresses
>>> metaprogramming and reflection (in the form of macros or MOPs, for
>>> example), because this gives you the way to reach underneath your
>>> current abstraction layer and influence the decisions there. And that's
>>> also why I currently think that threads + locking + means for
>>> synchronization may actually be the best Lisp dialects can offer.
>> Providing low-level constructs doesn't obviate the need for higher-
>> level ones as well. A box of parts is not a car.
>
> I'm not sure how to comment on this.

Programming is not like using a car, it's like building a car.

When you want to build a car, having a prebuilt car is not an
advantage: you'd have first to disassemble it, and they you would only
have the pieces needed to build this model of cars, not the wide range
of random pieces that would allow you to build whatever kind of cars
you may want to build.

On the other hand, with a box of parts, you can build whatever car you
want directly with no hasles.

--
__Pascal Bourguignon__

Bakul Shah

unread,
Feb 15, 2009, 5:49:51 PM2/15/09
to

Building everything from scratch every time gets to be a real pain
and is frequently error-prone. Appropriate sub-assemblies (or, if you
prefer, "abstractions") can usually help quite a bit. There is no
joy in discovering deadlocks for the nth time because different
components acquired locks in a different order. Even if I were as
smart as Dijkstra, Hoare or Brinch Hansen, it would be pointless for
my to rediscover _critical regions_ on my own when they have done a
superb job.

Pascal J. Bourguignon

unread,
Feb 15, 2009, 6:00:20 PM2/15/09
to
Bakul Shah <bakul+...@bitblocks.com> writes:

Who said there was only energy in the box of parts, or even only atoms?

--
__Pascal Bourguignon__

André Thieme

unread,
Feb 15, 2009, 6:57:20 PM2/15/09
to
Raffael Cavallaro schrieb:
> On 2009-02-13 14:41:14 -0500, André Thieme
> <address.good.un...@justmail.de> said:
>
>> we can do this in Clojure:
>> ((fn [n] (loop [x n, res 0]
>> (if (zero? x) res (recur (dec x) (+ x res)))))
>> 12345678)
>
> we can do this in common lisp:
>
> ((lambda (n) (loop for x from n downto 0 summing x)) 12345678)
>
> and when we do it in Clozure Common Lisp (dx86cl64) it runs 10 times as
> fast as your clojure code on the same machine.

I now had the time to measure it approximatly. I did not have access
to Clozure, but to SBCL64, which might perform even better?!

And there I found +/- the timing that you suggested.
Running it a few times in SBCL gave me:
* (time ((lambda (n) (loop for x from n downto 0 summing x)) 12345678))

Evaluation took:
0.060 seconds of real time
0.060000 seconds of total run time (0.060000 user, 0.000000 system)
100.00% CPU
160,589,730 processor cycles
0 bytes consed
76207888812681


On the same machine, I tested the 64-Bit Server-JVM with my code from
above:
user=> (time ((fn [n] (loop [x n, res 0]
(if (zero? x) res (recur (dec x) (+ x res)))))
12345678))
"Elapsed time: 360.236864 msecs"
76207888812681

Not only is my code longer, but also takes 5x more time than your
version.

If I try my shorter version, but with unboxed types I get:
user=> (time (loop [r (long 0) n (long 12345678)] (if (zero? n) r (recur
(+ r n) (dec n)))))
"Elapsed time: 32.118159 msecs"
76207888812681

With some declarations it may be possible to improve the SBCL version
further, so that it can outperform Clojure.

For fun I also tested Python and Ruby.

Summary:
1. Clojure: 33 msecs
2. SBCL: 60 msecs
3. Python 2.5.2: 2040 msecs
4. Ruby 1.8: 13620 msecs

All on a Intel(R) Core(TM)2 Duo CPU E7300 @ 2.66GHz running
Ubuntu 8.10 (64-Bit)

Rich Hickey

unread,
Feb 15, 2009, 7:20:57 PM2/15/09
to
On Feb 15, 4:38 pm, Pascal Costanza <p...@p-cos.net> wrote:
> Rich Hickey wrote:
> > On Feb 15, 11:13 am, Pascal Costanza <p...@p-cos.net> wrote:
> >> André Thieme wrote:
> >>> I think all major languages will get a STM with MVCC in the coming
> >>> months or years.
> >> That's not so clear yet. See "Software Transactional Memory: why is it
> >> only a research toy?" athttp://doi.acm.org/10.1145/1400214.1400228
>
> > That is a weak article. It criticizes STM in comparison to its
> > performance vs. serial execution. It doesn't compare the STM code with
> > equivalent versions using manual locking, neither for complexity nor
> > for performance. Nor does it place appropriate value on correctness.
> > People were (are!) skeptical of GC too, and there were (are) certainly
> > places where its performance rules it out. That doesn't negate its
> > value for people for whom its performance is sufficient and its
> > correctness lets them focus on their application domain.
>
> The article has some credibility because the authors have been doing
> research on STM themselves.
>

I didn't mean to imply they weren't credible. But you have to be
careful when transferring their conclusions to another domain. I've
met with some researchers in the HPC area where the dynamism of Lisp,
Java, even C++ virtual function calls, are all unacceptable for
performance reasons, and the authors' main argument against STM was
performance.

> I agree that is probably a good solution in the long run, but that's not
> clear yet and remains to be seen.
>

Agreed. Also, I do not think it is a universal solution, only one of a
suite of tools.

> >> Currently, the typical support in CL implementations is to provide
> >> threads + locking + some other forms of synchronization. It seems to me
> >> that these are the 'right' low-level facilities to build different
> >> high-level facilities on top.
>
> > I think it is tricky to get a good memory model defined that deals
> > with all of the issues. Java tries:
>
> >http://java.sun.com/docs/books/jls/third_edition/html/memory.html
>
> > See also:
>
> >http://jcip.net/
>
> > It's certainly much more subtle and difficult than just "threads +
> > locking".
>
> You're right, and I'm sorry if I gave the wrong impression here. (I used
> "threads + locking" as general placeholder for the bunch of operations
> typically provided in multithreaded CL implementations.)
>

But do they have well-defined memory models as above? You definitely
need those happens-before/memory-fence models to implement something
like an STM.

> >> Traditionally, the Lisp approach is not to provide one high-level
> >> solution for a particular programming problem, but to provide several
> >> different low-level means and give programmers the expressive power to
> >> build their own high-level abstractions on top. That's why Lisp stresses
> >> metaprogramming and reflection (in the form of macros or MOPs, for
> >> example), because this gives you the way to reach underneath your
> >> current abstraction layer and influence the decisions there. And that's
> >> also why I currently think that threads + locking + means for
> >> synchronization may actually be the best Lisp dialects can offer.
>
> > Providing low-level constructs doesn't obviate the need for higher-
> > level ones as well. A box of parts is not a car.
>
> I'm not sure how to comment on this.
>

I only meant to say - why is that the best they can offer? Why stop at
low-level support?

> >> Clojure is a very interesting project, and it's definitely interesting
> >> to have persistent datatypes at the core of the language. However, my
> >> main gripes with Clojure are the following:
>
> >> - It promotes purely functional programming. That's sometimes not
> >> appropriate.
>
> > I've never promoted Clojure as "purely functional programming", as I
> > am quite suspect of the concept implied by the words, and the term has
> > come to mean "having a type system that keeps the purely functional
> > parts separate". The presence of the reference types in Clojure makes
> > it clear that I don't think a purely functional style will get you all
> > the way there. I also know that the typical approach to state doesn't
> > either, especially when concurrency comes into play. But Clojure is
> > definitely an impure functional language. Promoting the use of
> > immutable data and pure functions is not the same as limiting you to
> > them.
>
> That's why I said "promote," and not "enforce." (I don't see static
> types as a necessary ingredient of purely functional programming, only
> the absence of effects.)
>

Then why the gripe? Plenty of Lisp/Scheme books promote using a
functional style.

> > When I give talks I emphasize the unique parts of Clojure because they
> > are interesting and that's where Clojure adds value, but that doesn't
> > mean they are the only parts. You can bash bits in Clojure just like
> > in CL and Java, there are mutable arrays etc, but what's interesting
> > about that?
>
> So how do you deal with concurrency when mutating arrays?
>

Is this a trick question? :)

I wrote Clojure's data structures so I would have something safe to
use concurrently, and I use them whenever possible. If I were to use a
mutable array, it would be within the confines of a function that
otherwise presented a functional interface, e.g. as an implementation
detail of sorting - the array would not be shared. The other case is
mutating an array once only on initialization, and never changing it
once shared. This is how Clojure's persistent data structures work,
and I've made the case they couldn't be nearly as fast if they were
implemented 'purely', i.e. only in terms of other immutable
structures.

Actual (overlapping) mutation of arrays in place, concurrently? I
avoid it. If you had to, you would need locks.

> >> - AFAICT, the only way to reflectively extend the language is by going
> >> down to the Java level. That shouldn't be necessary. A good Lisp dialect
> >> should allow you to stay within Lisp even if you want to influence the
> >> implementation of the language itself.
>
> > This is not true. The extensibility points that are defined in terms
> > of Java interfaces can be extended in Clojure or Java.
>
> OK, maybe I'm wrong here. So: Could you, in principle, implement the
> four reference types in Clojure itself in terms of lower level
> constructs provided by Clojure?
>

Extension and self-definition are two different things. But yes, you
could. Atoms were originally implemented in Clojure, and moved to Java
only to ease bootstrapping and consumption from Java. That's not to
say Clojure is as low-level as Java, so some extensions might not be
as fast as those written in Java, i.e. Clojure has no notion of
'volatile' and some of the other lowest-level constructs. There's
nothing that rules out Clojure becoming more low level, but the bottom
line is that static is faster (and uglier). Java already does static/
ugly pretty well, so is it worth it to add that ugliness to Clojure
for self-definition bragging rights? Thus far, it hasn't been worth it
to me. But certainly there are no limits for users - the bytecode
generation engine used by the Clojure compiler is embedded and
accessible by Clojure code - there are already parts of Clojure that
generate bytecode from Clojure, such bytecode having no limits in
performance or access to primitive constructs.

So, you're only a macro package away from an arbitrary bytecode
generator. Several are included with Clojure that generate classes and
interfaces for which you would otherwise have to drop into Java.

But I find the statement somewhat specious and preachy ("A good Lisp
dialect should..."), since I didn't find the kind of extensibility in
Common Lisp (in itself, or via well-defined interfaces in any
language) that I have in Clojure. CL has a hard-wired set of
privileged types. Syntactic extension is only one dimension of
extensibility (and supported by Clojure). CLOS is another, and I see
the kind of reflectivity you speak of in CLOS, but Common Lisp isn't
written in CLOS.

Could you write an efficient Common Lisp in CLOS, where the core data
structures and algorithms were extensible CLOS types and generic
functions?

> > At least Clojure's core
> > library functions are defined in a way that they can be extended to
> > other types.
>
> Well, I'm wondering about the ability to implement your own abstractions
> for parallel programming beyond the default ones provided by Clojure,
> within Clojure itself. If that's possible, I have to correct my
> statements about Clojure.
>

Well, concurrency constructs down to the CAS level, monitors,
reentrant read-write locks etc are all available in calls from Clojure
to core Java libraries, so you can do a tremendous amount of parallel
programming work in Clojure itself. Things like pmap, a concurrent,
semi-lazy, work-ahead version of map provided by Clojure, are written
in Clojure. Here's what it looks like:

(defn pmap
"Like map, except f is applied in parallel. Semi-lazy in that the
parallel computation stays ahead of the consumption, but doesn't
realize the entire result unless required. Only useful for
computationally intensive functions where the time of f dominates
the coordination overhead."
([f coll]
(let [n (+ 2 (.. Runtime getRuntime availableProcessors))
rets (map #(future (f %)) coll)
step (fn step [[x & xs :as vs] fs]
(if fs
(lazy-cons (deref x) (step xs (rest fs)))
(map deref vs)))]
(step rets (drop n rets))))
([f coll & colls]
(let [step (fn step [cs]
(when (every? seq cs)
(lazy-cons (map first cs) (step (map rest cs)))))]
(pmap #(apply f %) (step (cons coll colls))))))

There's one call to Java to get the processor count. Looks like
(really easy) concurrent programming in Lisp to me.

> Hm, it seems I have described Clojure too negatively. Sorry if that's
> the case.
>

No offense taken, but it seems there is a lot more to Clojure than you
might be aware of.

Rich

It is loading more messages.
0 new messages