
Racket vs Clojure (it's all about the data structures) - swannodette
http://programming-puzzler.blogspot.com/2010/08/racket-vs-clojure.html
======
sreque
I actually have very similar thoughts on clojure versus racket. I admire both
languges for different reasons, but if I were to pick one to get the job done
it would be Clojure. I cry every time I see the idiomatic construct in any
Scheme of converting a vector or string into a list just so you can do the
normal core operations on it, and then converting it back to a vector or
string, but this kind of code is quite common due to the design of almost
every Lisp.

Another issue I have with racket, which the article touched on a little, is
the overall lack of cohesion between different libraries and language
features. I believe this is an unfortunate consequence of starting out with
such a simple core language as all Scheme's do. Everything that is built has a
different style to it and doesn't necessarily interoperate well with other
parts of the language.

For example, structures are entirely orthogonal to objects and the class and
interface system are entirely orthogonal to units, even though these two pairs
of features can heavily overlap in their use cases.

When it comes down to it, if I want to get something done in Clojure I feel
like I have a clear path as to how to go about it, but in Racket it seems much
easier to get lost inside the language for the reasons mentioned in the
article.

~~~
Xurinos
> this kind of code is quite common due to the design of almost every Lisp

Scheme != Lisp (maybe a "lisp")

I was trying to figure out what you mean from a Common Lisp perspective. Are
you referring to the author's example?

    
    
      All of Clojure's collections can be treated as if they 
      were a list, and you can write algorithms to traverse 
      them using the same pattern of empty?/first/rest that 
      you'd use on a list. This means that all the powerful 
      higher-order functions like map/filter/reduce work just 
      as well on a vector as a list.
    
    

Just off the top of my head, CL's map and reduce can operate on lists or
vectors. Pretty much anywhere you see the word "sequence" in the spec, you can
work with a vector or a list (and several other types). I can understand the
point that it might be awkward in Racket; does it get awkward in CL?

I cannot speak for Racket/Scheme's limitations, but I am looking to see where
Clojure might be a better choice for me than CL other than interoperability
with Java. I have not seen a good data structures argument yet.

~~~
sedachv
CL ostensibly comes with a built-in sequence class. The problem is none of the
various utility methods that are included are generic functions, so in
practice it doesn't do much good. Christophe Rhodes worked on an extensible
sequence standard:

[http://www.doc.gold.ac.uk/~mas01cr/talks/2007-04-03%20Cambri...](http://www.doc.gold.ac.uk/~mas01cr/talks/2007-04-03%20Cambridge/ilc-
talk.pdf)

It's implemented in SBCL, I think there may be ports to other implementations.

This is the sort of thing the CDR (<http://cdr.eurolisp.org>) process is
supposed to address. Right now they only have proposals for a generic hash
table interface (<http://cdr.eurolisp.org/document/2/index.html> \- not
integrated with sequences!), and fixes to concatenate-sequence
(<http://cdr.eurolisp.org/document/3/index.html>).

In summary: CL comes with some nicer datastructures than Scheme (hash tables,
multidimensional and adjustable arrays), but other than that it has the same
problems.

~~~
Xurinos
That was good information. Thank you.

My takeaway for this is this -- If the utility methods are not generic
functions, it is fairly trivial to write a generic function that wraps around
the utility method and then play with additional types as needed. So if the
problem with the data structures in scheme and Lisp are that people are
spending a whole lot of time repeating conversion patterns, shouldn't those
people be developing abstractions, at least for their own use/libraries?

When I read this kind of complaint, I get the feeling people tend to want
Batteries Included Their Way. They do not go the one step further to wrap
things in the manner they desire. Like-minded folks do not get the benefit of
those wrappers. jQuery was a hit because someone deployed his library of
common JavaScript patterns.

~~~
sedachv
The problem is that any time you want to interface to code outside of that
library, you run into the same "convert vector to list" problem the OP was
talking about. The solution really is to cram down Rhodes' proposal into every
implementation's throat, so that all existing code can start handling new
sequence types.

------
gcv
While this is a decent article in its core argument, it's a bit misleading in
some of the details.

 _Methods are not true closures, and can't be passed directly to higher-order
functions._

You can trivially wrap a Java method in a fn, and Clojure has syntactic sugar
(the #() notation for anonymous function literals) to help with this.

 _Slow numeric performance._

This is true in the 1.2 release, but the next version of Clojure addresses
this problem in (IMO) a very elegant way. Search on the mailing list for
discussion of the num branch for more information. If numeric performance
concerns your problem domains, please use the branches that resolve this.

 _Slow startup time._

Yes, the JVM starts slowly. I restart Clojure REPLs about once a week. I don't
exactly notice this problem. Clojure isn't like Python or Ruby or Perl, where
you start new interpreters all the time.

------
Raphael_Amiard
Very interesting perspective. I never thought about it that way, but it's true
that one of my principal pleasures working with Clojure was about the data
structures, and their very nice integration into the language.

The concurrency features sounded exciting but were never of any use to me. But
the consistent and extremely powerful data structures and associated libraries
were my main subject of wonder, even if i didn't really notice it.

Now that i'm working with python all day, even though i really really like
this language, i can't help but hate the inconsistent way data structure works
(and the fact that they are mutable, and that mutable and immutable operations
are mixed up in the API without a clue). And i used to consider python as
being quite nice data-structures wise.

------
gruseom
This guy puts his finger on precisely the thing that might get me to switch
from Common Lisp to Clojure someday. The Java interop aspect is vastly
overrated on a technical level. (That's not to say it is overrated in general;
it's a marketing master stroke without which the language would have been
stillborn.) The concurrency innovations are at best experimental, seem
kitchen-sinky, and don't attract me much. But boy do I envy those data
structures.

~~~
macmac
> The concurrency innovations are at best experimental, seem kitchen-sinky,

Could you expand on that statement. I don't think there is anything
experimental about Clojure's concurrency features. They are quite robust and
build on proven technology.

~~~
gruseom
What I had in mind is close to what the OP says:

 _Clojure has a number of cool new ideas, but many of them are unproven, and
only time will tell whether they are truly valuable._

We don't know yet what will become the dominant concurrent programming
style(s). For all the talk about the multicore future, nothing has yet emerged
that's compellingly better than traditional approaches. Or maybe it has and it
just hasn't made its way through the noise into general consciousness; I don't
know. Clojure's approach here strikes me as a sort of experimental groping:
add several different new concurrency primitives and see what sticks. That
aspect seems quite different from the improvements Clojure offers that came
from years of experience programming Common Lisp and a resultingly deep
intuition about what constructs would make it more comfortable.

Perhaps Clojure has hit the concurrency jackpot and come up with a better way
of writing parallel programs. That would be significant. It would even be
significant if it offered a better way of writing a particular class of
parallel programs. So far, though, I haven't seen convincing evidence of that.
I'm more excited about Clojure being an incremental improvement over Common
Lisp that is winning over a new audience of hackers.

------
evanrmurphy
Racket's hygienic macro system seems to be a heavier cognitive load as well
(although it certainly has its own benefits, and some would say it's
essential). I really like in Clojure and Arc how easy it is to pound out a
macro when you need one.

I have less experience in CL and Racket. I would think that since CL's macros
are unhygienic, they would be easy to write as they are in Clojure. It could
be that I just haven't gotten the hang of Racket's macros yet, and that once
you do they aren't any harder. Let me know if this is the case.

~~~
waterhouse
For purposes of writing macros, CL seems to be identical to Arc, other than
the name for the macro-defining construct ("defmacro" instead of "mac").
Which, of course, can be fixed with a macro.

There are three "things" about Racket's macro system: hygiene, pattern-
matching, and phases. The sort of built-for-you way to do things (define-
syntax with syntax-rules or syntax-case) is both hygienic and pattern-
matching-based. But you can also make something that is hygienic but not
pattern-based, or pattern-based but not hygienic, or neither; this last one is
done for you in the library "mzlib/defmacro".

However, what you _can't_ do, at least not without doing something like
creating a new file and inserting (require (for-meta 2 <new file>)), is define
a macro that you use in a macro body. For example, if you wanted to define
"w/uniq", so that you could write

    
    
      (define-macro (swap a b)
        (w/uniq tmp           ;instead of (let ((tmp (gensym)))
          `(let ((,tmp ,a))
              ...etc
    

Simply writing (define-macro (w/uniq var . body) ...) will not work; a macro
must be defined at phase level 2 for it to be used by a macro that is defined
at phase level 1 (which is when macros are normally defined). And I have not
succeeded in finding any way to make this work other than, as I said, creating
another file and requiring it. (And, of course, if you want to use anything
that isn't base Racket when defining 'w/uniq or whatever, you'll have to
require that somehow, or do without.) This might be a minor complaint, but it
does annoy me.

See also: <http://arclanguage.org/item?id=11529>

~~~
samth
First, you don't need `w/uniq' in Racket - hygenic macros do that for you
automatically. Here's `swap' in Racket:

    
    
      (define-syntax-rule (swap a b)
        (let ([tmp a]) (set! a b) (set! b tmp)))
    

But if you wanted to implement it, here's an easy way to do it in the same
module

    
    
      (define-syntax (swap stx)
        (define-syntax (w/uniq stx) ...)
        (syntax-parse stx
          [(swap a b) ...]))
    

That said, the fact that you can't define macros to be used in macros in the
same file can be annoying, and is something that we plan to add to the
language.

------
vdm
Where Racket has a clear edge on Clojure is learning about it. <http://racket-
lang.org/> is very good, and the included IDE means you can hit the ground
running and start typing code from a tutorial within minutes.

Clojure by comparison is like moving to a new city and trying to make friends
(with emacs).

~~~
evanrmurphy
Racket is good that way, but you can get immediate gratification from things
like Try Clojure too.

And of course, the Clojure setup is actually attractive if you're already
using Emacs, especially since it offers integration with SLIME. No one has
been able to do that with Arc yet, as far as I know. (With Racket, I'm not
sure.)

------
zephjc
"Frankly, I have yet to find a Java library I'd actually want to use."

HyperGraphDB, and the Google App Engine jars, for starters.

------
singular
I had horrific experiences trying to get Racket namespaces to work correctly
(in Racket, a namespace is a value rather than simply a name). I found I
couldn't get files to use the same namespace no matter how hard I tried, and
without a specific namespace some code I was eval-ing simply refused to work.

I suspect Clojure lacks this 'feature' :-)

It's frustrating because I'm sure Racket has a lot to offer but not being able
to do something really simple (even after hours and hours of trying)
completely put me off.

------
c00p3r
_Some people love Clojure specifically because it sits on top of Java and
gives them access to their favorite Java libraries. Frankly, I have yet to
find a Java library I'd actually want to use. Something about Java seems to
turn every library into an insanely complex explosion of classes, and Java
programmers mistakenly seem to think that JavaDoc-produced lists of every
single class and method constitutes "good documentation"._

Amen!

~~~
tmountain
Love it or hate it, Java's back catalog of libraries is gargantuan. One of the
biggest obstacles in getting a new language adopted is library support. The
fact that Clojure gets all that for free is a way to get people using the
language now. As Clojure matures, I'm certain more and more native libraries
will emerge negating a lot of the need for Java.

~~~
kunley
quantity != quality

~~~
mhd
I absolutely agree, but if you have no choice but to cooperate with the usual
"enterprise" hodgepodge with its myriads of standards and protocols, not
having to write something that will allow you to do that, is a huge benefit.

You'll have to suffer the environment, you don't have to suffer the language.

But I think it's going to be interesting whether the clojure community will
re-write the innards of the libraries they're wrapping already. Will we see
e.g. compojure that's turtles all the way down?

