I've just released Vertigo [1], which I describe in this thread: https://groups.google.com/forum/#!topic/clojure/BayfuaqMzvs. I suspect this has some bearing on the conversation.
Zach [1] https://github.com/ztellman/vertigo On Tuesday, July 9, 2013 8:11:58 AM UTC-7, Alexander Gunnarson wrote: > > Hello everyone! It's great to be here with all you fellow Clojurians. Just > so you know, this is my first post on this group, so don't shoot me if it's > terrible ;) > > As background, I've been working through SICP and have been loving Scheme. > It's almost breathtaking how elegant and clean the code can be (I had some > moments > like xkcd goes off abou <http://xkcd.com/224/>t). Of course, though > Scheme is beautiful and simple, everyone knows that it's not especially > practical, in general. I love the Lisp paradigms but I'm not really a fan > of CL so I did some looking around and stumbled upon Clojure. It seems that > Clojure really has a lot going for it between shockingly easy concurrency > support and Java interop, among other things. But one thing that's been > bothering me is that it seems like to optimize performance in Clojure, you > have to sacrifice some elegance. > > *Example 1: Tail-call recursion* > > *Scheme* > One example would be tail-call recursion. For instance, normally in Scheme > I'd naively implement an iterative exponent function like this: > > (define (expt x n) > > (cond ((= 0 n) 1) > > ((= 1 n) x) > > (else (expt (* x x) (- n 1))))) > > Pure. Simple. Beautiful. (Not that I'm the best Scheme programmer ever, > but to me it looks beautiful, and it conforms well to the base of the > problem. You get the point.) > > *Clojure* > Of course, tail-call recursion is not possible with JVM, so Clojure uses a > *recur* macro in place of direct recursive function calling. It avoids > blowing the stack as quickly but it's still not 100% "mathematically pure" > in the way Scheme tends to be. > > An added gripe is that the* else *form within *cond *in Clojure uses a > keyword, *:else*, instead of the more consistent parenthetical form used > in Scheme. I suppose that's to make it less "Lispy." But it just ends up > making it a little less elegant. > > *Example 2: Type casting* > * > * > Some have said that Clojure can be somewhat slow (as with all Lisps). I'm > not sure how true this is, but I stumbled on an example on John Lawrence > Aspden's > blog<http://www.learningclojure.com/2013/02/clojure-is-fast-is-clojure-still-fast.html>. > > He wrote a program to implement Euler's method like so: > > *First Solution* > > (defn f [t y] (- t y)) > > (defn solveit [t0 y0 h its] > (if (> its 0) > (let [t1 (+ t0 h) > y1 (+ y0 (* h (f t0 y0)))] > (recur t1 y1 h (dec its))) > [t0 y0 h its])) > > > He points out that "if this was an assembly language program that worked the > way you'd expect, each loop would take 7 cycles." So he tests it for Clojure. > The result? On his netbook with Clojure 1.4: 2400 cycles. As he says, "We're > looking at a slowdown of about 300 times over what we could probably achieve > coding in assembler or in C with a good optimizing compiler." That's not > surprising, I suppose, but it's still a little disheartening. After all, you > want your language to be fast, right? Well, after a few iterations, he > manages to reduce the cycles way down - all the way down, in fact, to 37, > which is quite a feat. Like so: > > > *Final Solution* > > (defn solveit-4 [t0 y0 h its] > (let [zero (long 0)] > (loop [t0 (double t0) y0 (double y0) h (double h) its (long its)] > (if (> its zero) > (let [t1 (+ t0 h) > y1 (+ y0 (* h (- t0 y0)))] > (recur t1 y1 h (dec its))) > [t0 y0 h its])))) > > > But the thing is, between the *recur *macro, explicit typecasting, and the > *loop* construct, yes, you have a very significant performance increase, but > it the code's gotten bigger, much less readable, and much less elegant. > > > *The bottom line* > > * > * > > My idea, which is probably very naive, but one which I'm curious about, is: > > *Is it possible to have some sort of set of automatic-optimizing macros that > work on Clojure code to preserve elegance while maximizing performance?* > > * > * > > In theory, it would be kind of an abstraction layer. There would be one file > that would store the code that gets read and another output file that stores > the code that actually gets evaluated by the REPL, and a set of macros to > optimize the "front-end", "abstracted" file into the output, "nuts-and-bolts" > file to be evaluated by the REPL. Probably this would be a very intensive > process - I don't know. But maybe it's worth the trouble after all to save a > ton of programmer-hours by increasing readability. > > > Thoughts? > > -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.