Racket vs Clojure (it's all about the data structures)
programming-puzzler.blogspot.com
programming-puzzler.blogspot.com
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.
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.
http://www.doc.gold.ac.uk/~mas01cr/talks/2007-04-03%20Cambri...
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.
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.
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.
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.
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.
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.
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.
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
(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.Clojure by comparison is like moving to a new city and trying to make friends (with emacs).
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.)
HyperGraphDB, and the Google App Engine jars, for starters.
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.
Amen!
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?
http://en.wikipedia.org/wiki/Sturgeon%27s_Law says "90% of everything is crap".
On the JVM that number might be a bit higher, but there are some excellent gems in there too. Lucene comes to mind especially; there's nothing like it on any other platform.
But the flipside of this is positive. It feels incredibly good to take a Java library down into your mad scientist workshop and come back out with a beautiful, functional, efficient, and useful interface to a previously ugly library interface. A great example of this is how most Clojure programmers use Jetty. Just pass in a lambda!
And then sometimes people have written Clojure wrappers around Java frameworks. Ring/Compojure as a thin layer over Java servlets is a good example. clojure.contrib.sql is a thin wrapper around JDBC, so that Clojure can already talk to a wide variety of databases.
In that case you get the benefit of a solid codebase as well as the benefit of a clean Lispy interface. Not sure how much more you could ask for. It would likely take years to reproduce all of that work in pure Clojure.