Hacker News new | past | comments | ask | show | jobs | submit login
Thinking in Clojure for Java Programmers (factual.com)
105 points by swannodette on Dec 16, 2010 | hide | past | favorite | 50 comments



There really should be a comparison to something other than java, it doesn't seem fair.


Fair point! Well, Google will show you plenty of results for, e.g. "clojure vs. scala". What I was going for wasn't an unfair comparison, but rather a kind of bridge for Java / JVM folks who might be interested in a more functional way of life. My bad if I missed the mark. :-/


I don't think it's an unfair comparison at all. Comparing Clojure and Scala can only generate interest in a few small (though growing) communities.

You've done an excellent job of showing Clojure's benefits to a much, much wider audience - programmers not familiar with functional programming. Also it's compelling to hear that you're successfully using Clojure to efficiently "script" larger Java codebases.

Kudos for writing such a hype-less, non-condescending, and educational tutorial for Java/imperative programmers!


Thanks swannodette, very kind of you! Yes, it's been cool to layer Clojure on top of legacy Java and see what happens. Some want to sell Clojure as "a better Java than Java"... I think that may be a little misguided. But, there are certain situations where it rings true... at least IMHO


I don't think Clojure's goal is to be "better Java". Rather, that for whatever task you might consider Java, writing that task in Clojure should be:

  * easier to write
  * perform just as well
  * be concurrency safe
Clojure isn't there yet, but it's well on its way I think.


it's easier to have unsafe concurrency in clojure. It's certainly easier to get it right by default than in java, but it's still very easy to introduce a race condition or some such.


Typo, I meant easy not easier


true but you can use it to script java witch would be better then doing it in java.


Clojure isn't really 'layered on Java'; it does however run in the JVM (just like Java).


I think its a fine article - it delivered exactly what the title said. It wasn't "Thinking in Clojure for programmers of all imperative or OO languages".


Java developer here, I like it so far. Your post got me quite excited but I'm not really sure where to go from here.


Yeah, I can see what you mean. My question to ZoFreX and those in a similar situation: what specific questions / fears / needs should be addressed in a good Part 2?


I love functional programming. At least the functional parts of Python. However, Lisp seems better suited for computers than for humans.

Which is probably a side effect of having been created when 64kB was a decent memory size.


Please justify your claim: In what way is Lisp (in contrast to Python) better suited for computers than for humans?


2 + 3, touche :)


That holds water only at the most sugary syntactic level.

At the most simplistic semantic level a binary addition operator is a simple mapping of what the machine wants to do (take 2 registers and put their sum in an accumulator register).

Lets expand that simple piece of code. Now you have more than 2 numbers to sum:

   2 + 3 + 40
you have been forced to encode both stages of summing the first pair of numbers and then the result and the third value. Contrast this with a lisp

  (+ 2 3 40)
Lisp has takes care of all of the details for me; i merely need to supply the operands and the language worries about mapping it to the underlying operations as needed.

Clearly, here is a situation where lisp notation elevates me from having to think about the machine.

Touche, indeed.


This is a simple expression. How about writing (1+2) * ((3 + 6) / (1+2)) in prefix notation without losing readability?

When precedence is to be overridden, the infix form can be easily understood by anyone with basic knowledge of arithmetic. Prefix on the other hand can be confusing. This makes BigDecimal arithmetic in Java cumbersome compared to languages which allow operator overloading.


1 2 + 3 6 + 1 2 + / *

;)


prefix, not postfix:

    (* (+ 1 2) (/ (+ 3 6) (+ 1 2)))
I was also skeptical regarding the readability of prefix notation vs. postfix for large problems. However, when I tried it, I actually found it easier to handle. For example, here's a very obnoxious heat transfer correlation that I typed in common lisp:

    ( * 4.364
        (expt (1+ (expt (/ Gz 29.6) 2)) 1/6)
        (expt
            (1+ (expt (/ (/ Gz 19.04)
                         (* (expt (1+ (expt (/ *Pr* 0.0207) 2/3)) 1/2)
                            (expt (1+ (expt (/ Gz 29.6) 2)) 1/3)))
                3/2)) 1/3 ))
Versus, in infix it's more like:

    4.364 * (1. + (Gz/29.6)**2)**(1/6) 
          * (1 + ( Gz/19.4/(
                 (1 + (Pr/0.0207)**(2/3) )**(1/2)
               * (1 + (Gz/29.6)**2 )**(1/3)
            ))**(2/3) )**(1/3)
Now, I admit that my sense of whitespace is pretty inconsistent, but still: Here is a real case of a very annoyingly nested expression in both infix and prefix notation. While I think it would take quite a while for anyone to sort out what the Hell is going on for either of these regardless of experience (versus, say, rendered LaTeX), I would put forth the suggestion that the common lisp is actually easier to understand.


Bullshit. Everyone is taught the first way, everyone writes the first way by hand. The first way is the "human" way. The second way is the obscure theory-of-computation way. Your post seems like pure rationalization. Even if it applies to you, I can't see how you can claim with a straight face that prefix math is more "human" in general.


Math notation has gone through a decent bit of change over time, and has proven amenable to change to suit the needs of specific environments. Lisps don't force you to use "(+ 2 3 4)" instead of "2 + 3 + 4" to be contrary, it's there to preserve a cornerstone of the language family: the power gained by easy manipulation of syntax trees.

Also, math notation is pretty much the only bad syntax example when comparing with other programming languages. It becomes literally the only example when you leave hard asceticism and take the approach that Clojure does of providing literals for vectors, maps, and sets.

So, yeah, maybe dig a little deeper next time instead of dismissing tools out of hand? Many programs never directly perform arithmetic, are Lisp dialects unsuitable for those as well?


I understand why it does it that way; I've written programs in Clojure and quite like it (with math expressions I've been using "let" a lot to break them up and make them more readable, but this also makes them a lot more verbose). But contrary to the GP, I still see prefix math as a cost, not a benefit.

edit: I love Clojure's literals but I still think even with editor help, nested parens are a less readable form of structure than c-style blocks. I understand why it's done that way (uniformity for macros), but that doesn't mean I have to like it in and of itself.


Moreover, prefix notation originally comes from maths. It was invented in 1920:

http://en.wikipedia.org/wiki/Polish_notation


> Everyone is taught the first way

Obviously neither one is more "human"; humans don't know the first thing about math until they're educated. This has everything to do with what's familiar and nothing to do with what's "natural" to the human mind.


If everyone is educated the same way, that's the human way of doing it, at least in the sense of human usability. It's irrelevant if we could theoretically do it another way. Language is learnt too, but that doesn't mean that (for English speakers) writing our programs with kanji keywords would be just as usable and readable as writing them with English ones.


To summarize your posts in this thread, if you have a representation (R) and you discover a new representation (R') where R' is provably better than R. R' however has a cost associated with converting to it, lets call it the impedance.

My reading of your posts is that you advocate ignoring any R' that has an impedance greater than zero. Surely it would be wiser to evaluate the switch to R' if the value of R' less the impedance of R' is positive?

Is this a fair representation of your position?


No, it's neither fair nor accurate. Anyway, I see no point in continuing this conversation against a bunch of Lisp fans who will accept any old nonsense if it's pro-Lisp.


Everyone isn't educated the same way, sorry. That invalidates your entire argument, doesn't it?

"One day I asked him, 'Roger [Hui], do you do math in English or Cantonese?' He smiled at me and said, 'I do it in Cantonese because it's faster and it's completely regular.'" - "A Conversation with Arthur Whitney"


I think your defining "human" based on norm. I would prefer to define "human" based on a humans ease of use given familiarity with the notation. I prefer this definition because if you use what I take to be your definition you get oddities like Roman Numerals being more human than what we use today. Given this definition I'm not sure you can say he is purely rationalizing his choice, since he gives a clear example of a situation in which he finds that the notation is easier to use.

I don't feel strong one way or another on this issue, though I do program in Clojure. If you want infix math in clojure: http://data-sorcery.org/2010/05/14/infix-math/


Base 12 is better than base 10, but that doesn't necessarily mean it's worth the cost of using it in our programs (I realize that people do use other bases in some contexts, but that's actually an example of coding to the computer's standards).


If you REALLY need infix notation in Clojure, Fogus' new book has some an example infix function at https://github.com/fogus/unfix/blob/master/src/joy/unfix/inf... which you use like:

    (infix [1 + 2] * 3)
    ;=> 9


I don't write my equations by-hand the same way I do on the computer. For example:

    ( (1 + 2)/(3 + 4) )**2
vs.

       ,                .  2
      /      1 + 2       \
     |    -------------   |
      \       3 + 4      /
The difference may seem subtle, but it's shockingly important. A better example may be

      ,-, b
      \
       \    x + 1  dx
        \
     '--' a
instead of something like

    trapz(a, b, @(x) x+1);
The truly human way of writing down math requires much more flexibility than a typical text editor can really afford. This isn't to say that some sort of pseudo-visual programming is Teh Futar though.

(I do think a LaTeX --> solved expression tool would be awexome though.)


Do you say "add 2 and 3" or "2 add 3"?


I admire prefix notation but that's a poor example. Most people say "2 plus 3" and the prefix version is really wacky: "plus 2 3".


They say "2 plus 3" when they read out loud the standard mathematical infix notation for that expression but that's not what they do.


Indeed. For example, retaining a method name like "defn" seems more than a little suspect in an era of gigabyte memories, terabyte hard drives, and auto-complete IDEs.


'defn' is hardly old-school; Python uses 'def' to define functions. Now 'car' and 'cdr' on the other hand...

This is far preferable to typing e.g. "public static void ..." all the time


defn harkens back to an era when using a computer meant using a teletype, putting an extreme premium on terseness in naming. And it's just an example, car, cdr, cons, etc. are just as bad. It's easy to memorize these but that doesn't mean they are the best choices for readability, writability, etc.

I have a hard time believing that the best choice of name for all of these operators just so happens to always be some 3 or 4 character string that's almost but not quite a word and seems to have dropped some vowels.

As for "public static void ...", that's a disease of another sort, equally as bad.


I disagree. If car and cdr and cons are bad, it's because they don't look or sound like what they do, and they have good replacements, like first and rest. defn is a macro that makes it simpler to def, which sounds and looks like "define", a binding to a fn, which sounds and looks like "function". I don't think you get any clearer than that without writing "define-function" everywhere. And I don't want to write "define-function" everywhere.

Do you think that every index variable in a loop should be called "index"?


`defn' is a mnemonic for "define function". `cons' is a mnemonic for "construct".

`car', and `cdr' and replaced in clojure with `first' and `rest'.

Once you've spent ten minutes with the language, these names aren't going to trip you up.


People want to learn "new languages" without having to learn anything new.


Well put. There's also a tradeoff between learning-curve and proficiency in language design: many features that aid the one detract from the other. I think this causes much of the noise in programming language discussions, especially with Lisp.


I think using defn as an example here is a poor choice for this argument. defn is not an abbreviation of `define` or 'defun', instead it is a fusion of `def` and `fn`. fn is clojure's anonymous function form and def binds a var to a value. You can see this via macroexpand:

   user=> (macroexpand '(defn f [x] x))
   (def f (.withMeta (clojure.core/fn f ([x] x)) (.meta (var f))))
[edit: clarified an ambiguous initial statement]


But we still read with the Mark I Eyeball. A commonly-used keyword should be short, and in a functional language, using "fn" instead of "function" seems a perfectly sensible idea.

Mathematics and natural languages take the same approach, assigning the most commonly used bits of the language the smallest words/symbols. It makes the document easier to parse, when done within reason.


defn is not retained from anywhere. It was coined by Clojure. I don't find it particularly shocking. I also consider the ability to write code effectively outside of an autocompleting IDE a feature, not a bug.


Autocomplete and debugging with clojure works. The problem is most clojure developers don't use common java IDEs. So it appears like it doesn't work or exist. But recently there has been an explosion of plugins to help everyone that isn't familiar with emacs & slime.

Autocomplete:

http://stackoverflow.com/questions/4289480/how-to-do-automat...

Debugging:

http://clojure.org/getting_started

Plugins:

https://github.com/swannodette/textmate-clojure

http://code.google.com/p/counterclockwise/

http://plugins.intellij.net/plugin/?id=4050

Options:

[emacs is too hard on windows] http://clojure.bighugh.com/

[but I use a mac] http://aquamacs.org/

[but I write ###.NET] https://github.com/richhickey/clojure-clr https://github.com/jmis/vsClojure


That's the point: you need auto-complete IDEs, terabyte hard-drives and gigabyte memories to program in Java.

You don't need any of that to program with Lisp (although Emacs and SLIME can help)


I like it for screen real estate purposes. The shorter stuff is, the more text I can view in a single window.

I wish monitor real estate progressed at the rate of Moore's law, but I suppose we'd need bigger rooms to put them in before too long.


I quite agree. And we can extend this priciple, so that

  a * b
would be replaced by:

  multiply(a, b)
which is much more self-documenting :-)


i prefer short names for methods you use so often that there is no chance of forgetting what it means.




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

Search: