HN2new | past | comments | ask | show | jobs | submitlogin
Clojure++ (notes from Rich Hickey talk) (combinate.us)
98 points by puredanger on Sept 27, 2010 | hide | past | favorite | 42 comments


The changes are pretty substantial when you need them. I wrote a clojure implementation of Base64 that's faster than apache's commons-codec. In 1.2 it was about 5x slower.


In what version of clojure could you write it with the same speed?


Couldn't you speed up loops without annotations by compiling two versions: one that uses longs and one that uses bignums. You start in the loop that uses longs and before an operation that can overflow you check whether it overflows and if so you jump to the loop that uses bignums. This way you only suffer a few extra checks which is a lot cheaper than boxed numbers.


Hmmm, Kawa's been doing this for better part of a decade. I tried this in kawa:

    (def (fib n :: <long>) :: <long>
        (if (<= n 1) 1
            (+ (fib (- n 1)) (fib (- n 2))
Almost identical speed results to Rich's "improved" version.


Rich is not trying to improve over Kawa. He's trying to improve over previous versions of Clojure.


We are in Clojure Alpha 1.3 v1 and allready the same speed.

The did it for a dacade and rich does it for a couple months and you bash him? We arn't the kawa guys faster after a decade.

Clojure is faster in other stoff like interop witch is really importend on the JVM. Can Kawa write down-to-java speed types?


> We arn't the kawa guys faster after a decade.

Kawa guy. Kawa was written by a single person, Per Bother, in his spare time. Clojure has a massively larger mindshare and collection of coders behind it. Ask yourself: why has Clojure been so slow for so long?

> Clojure is faster in other stoff like interop witch is really importend on the JVM.

Actually in my experience this is where Clojure is very slow indeed. If Clojure has to convert everything to Refs, it gets extremely slow (in our tests on my simulation system, up to 1000x slower than Kawa. I am not making up that number.).

> Can Kawa write down-to-java speed types?

Absolutely.


But: (defn ^:static fib ^long [^long n]) is really not sexy.


The performance alternative was dipping into java. It's sexy enough.


Isn't "sexy enough" Clojure's motto?


its better then optimicing CL. But you write you will only to that if you really need the bare metal performance. I want do that in day to day code.

The ^long thing is pretty cool bettr then : <long> or something. Its nice that it is just metadata.


shouldn't it be called (++ clojure) or something like it?


Or (inc clojure) perhaps? Isn't (++) an abomination in functional programming with immutable state?


(swap! clojure inc) is acceptable and a more literal translation, although refactoring your code to use (inc clojure) is usually more idiomatic.

See also, ADD 1 TO COBOL GIVING COBOL.


My point was not to translate the method name, it was to point out that (++) is anti-Clojure. I wanted to refactor the title to avoid mutability. I.e., "return the increment of clojure", not "change the value of clojure".


I have heard "POST-INCREMENT COBOL BY 1"


++ isn't defined in Clojure, and in Haskell ++ is used to concatenate lists. Using (inc) isn't considered an abomination, though.


"(inc clojure)".

which is probably really good marketing. (About e.g. "succ ml", I'm not sure.)


Well, I agree with you that isn't fantastic marketing. But neither is using a mutator function name to describe a talk on a functional language.

Rich Hickey loves to rail against nonsensical constructs/concepts like @date.day = 3, and I'm guessing he is very against the (++) method, as well. (How can you tell one number to become another number?)


I haven't done anything with Clojure, though I'm curious and wondering if would be worth my while. I'm already familiar with languages covering similar territory (Erlang, other Lisp dialects), but I haven't done much with the JVM. Also, the lack of tail-call optimization in Clojure is off-putting to me, but it looks several good design trade-offs elsewhere would probably make up for it.

(I was thinking of tattoos, PS.)


I did not get the tatoo reference, haha. As far as I understand, though, there is TCO in Clojure, right? You just have to be explicit and use (recur).


That's not TCO, that's just an optimization for individual recursion. You might be able to use it for multiple forms under the same recur (like w/ "labels"), but it's very different when it applies to everything.

With TCO, you can use a set of mutually-recursive functions - you can set up a FSM via tail calls, use CPS for backtracking (and many other things), a re-entrant virtual machine where each opcode is a tail call (rather than a giant switch/case or lots of gotos), etc. Being able to use function calls like "gotos with arguments" (but with local scopes, etc.) can make some constructs much easier to read, reason about, etc.

I'm not that familiar with the implementation of the JVM, but AFAICT it's due to difficulties setting up tail calls efficiently in JVM bytecode.


Rich is very mutch pro tail calls. Look at his talk at the JVM Language http://wiki.jvmlangsummit.com/What_the_JVM_needs (the video is not online jet).

As soon as the JVM supports it Clojure will support it. For me recur and trampoline are quite ok. I would want to miss on clojure because of TCO but I don't know what would be possible with TCO. I hope I can learn that in one of the books I have here :)


Any thoughts on the question I made before that (https://hackernews.hn/item?id=1736446)?

And yeah, I thought of mentioning trampolines, didn't know that it already had a readymade one. Sometimes they add too much overhead, but often it's still a good trade-off.


Do you mean the question if it was worth your while? If it was I would say yes because its a modern list that fixes old lisp problems, learn from the new languages and is pretty fast.


You can do optimized mutual-recursion in Clojure as well, using (trampoline). As with the optimized self-recursion using (recur), it's more work and not as nice as built-in TCO, but this is likely as good as it gets for now, given the current limitations of the JVM.


I guess the right thing to do would be to claim I was being ironic here, but really, it was just a poorly thought out late night titling decision.

If I could do it again, I'd go with (inc clojure), as below.


thats not the officale name. Its just clojure 1.3. The author choisse the name because of the groofy++ thing.


"... most numeric functions in Clojure, will no longer auto-promote values to Big numbers..."

Oh no! Clojure is choosing the route of premature opt. What a terrible shame.


I was at the talk in question. Some more context:

Bignums "contaminate" surrounding operations. Adding a long and a bignum yields a bignum. Seeding an equation with a single bignum (42N is a bignum representation of 42) will prevent overflow.

No one has come forward with a single real-world scenario where they're using bignums in Clojure. Choosing a default that is only theoretically useful rather than a 10x performance improvement seems a bit silly.

In any situation where the compiler cannot be sure you're using primitives everywhere, it will emit non-overflowing bytecode.

By my measure, there's nothing premature about this optimization. In Rich Hickey's words, Clojure is a replacement for Java, not Ruby. Giving up Java-like performance makes the language quantitatively less useful.


That Clojure's target is to replace slow old Ruby (actually that never happens, rather a successful language gains dominance in some niche and spreads from there) is only more evidence that it isn't worth breaking the abstraction called "numbers" to get merely an order of magnitude better performance. Seriously, I almost can't believe this is still an open issue in 2010. And Rich Hickey, who has designed such a great language otherwise, is on the wrong side of it? I'm boggled.

EDIT: If peregrine's comment is right, then we're arguing about something that isn't happening. I hope so.


Clojure's target is to replace slow old Ruby

That is very clearly the opposite of what I wrote.


Oh, sorry! I transposed the words.


Premature? No people have been complaining (or just getting very confused) about Clojure's numeric performance without oodles of invasive type-hinting for two years now.

More importantly with these changes it's now possible to reimplement Clojure's core datastructures in Clojure without sacrificing performance.

The majority of people who care about BigInts are solving Project Euler problems, not writing Clojure libraries or deploying apps into production.


2^63 numbers ought to be enough for anyone, huh?


The situations that deal with numbers that large are usually a bit nuanced. (i.e., "it's 1 in 2^63 of the time that I need bignums for day-to-day programming"). I agree that losing accuracy needlessly sucks (and I've been burned by floating-point), but 64 bit ints are usually good enough.

I'm thirsty for counterexamples.


This is only true if you use the annotations, at least that is my understanding.


Really? That'd be fantastic. Did I misunderstand this?


Yes the annotations are purely optional.


Right, but the initial quote seemed to say that promoting to BigNums would no longer be automatic. But I may have misunderstood.


Correct, promotion will no longer be automatic because the semantics of primitive and auto-promoting arithmetic can't be unified (on the JVM with no fixnums, the return types are disjoint). However, missing from the blog post and discussion here is the fact that, should there be overflow, an exception will be thrown. We are not adopting the silent error strategy of C/Java etc.


We don't need Scheme++ to implement .... (c) Brian Harvey, a famous CS61A teacher. ^_^




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: