Hacker News new | past | comments | ask | show | jobs | submit login
Java.next() - Clojure: The Return of the Lispers (batsov.com)
79 points by bozhidar on May 16, 2011 | hide | past | favorite | 37 comments



I'd love to be sold on a new language, but using it by showing me ridiculous examples of a language I know reduces the credibility of the seller. It's an otherwise good looking article but when I see this:

  public boolean hasUpperCase(String word) {
    if (word == null) {
        return false;
    }
    int len = word.length();
    for (int i = 0; i < len; i++) {
        if (Character.isUpperCase(word.charAt(i))) {
            return true;
        }
    }
    return false;
  }
Where it should be this:

  public boolean hasUpperCase(String word) {
     return word != null && word.matches(".*[A-Z]+.*");
  }
It casts doubt on the other examples. I'm not saying Java isn't a verbose language that can get tedious, but let's use some decent examples.


It's doesn't cast doubt on the examples at all, your code is less generic:

  (defn has-uppercase? [string]
    (some #(Character/isUpperCase %) string))
This code can deal with any sequence of characters: String, Array, PersistentList, PersistentVector, Cons, LazySequence, etc. Your example only deals with String.


Fair enough, but mine was not an alternate to his Clojure code, but to his Java code (which only checks String). Perhaps he should have focused on the functional efficiency like you did, rather than just bloated lines of code.


It's still not an alternate to the Java, since there are more upper-case characters than the 26 your regex matches. But I'm sure there is an appropriate Unicorn character class you could use instead.


... A String is a sequence of characters. Why would you want a "list of characters"? That's called a String.


Useful if you are composing the function with others which may not be built specifically for strings, e.g. filter, map etc.


Your revision doesn't handle non-English uppercase characters. Character.isUpperCase() does.


Stuart Holloway uses something close to this in one of the presentations at infoq.com. He uses a method from Apache Commons stringUtils as an example of battle-tested enterprise java.

Whether or not that is truly representative of java code is perhaps another question but there is certainly a body of similar accepted code out there eg http://commons.apache.org/lang/api-2.5/src-html/org/apache/c....


Your example suffers from the fact that it is not an example for anything. It's just a special case and it's incorrect on top of that. The example he gives shows the power of lambdas and the generalized concept of sequences perfectly well.


Actually it's an example taken from here:

http://www.jarvana.com/jarvana/view/org/apache/axis2/axis2-k...

But i don't know whats less performant, your reimplementation with regexes or the clojure sample.


I don't know Clojure, but it certainly reads as a direct translation of the original implementation, which is about as fast as you could make it in Java.

The Regex version, while similarly terse would be massively slower.

I agree the example is a bit odd, but it does serve the purpose of comparing equivalent code, which I believe is the whole point.

It does do the job of showing that simple (and by inference (?) not so simple) algorithms can be more succinctly expressed in Clojure than in Java. (not that that is particularly difficult)


The regex version is indeed slower, 5-20x depending on the length of the string and the location of the uppercase character, etc.

  > It does do the job of showing that simple (and by inference (?) not so simple)
  > algorithms can be more succinctly expressed in Clojure than in Java. (not that
  > that is particularly difficult)
My point was not that Java is/can be as concise as Clojure (or Ruby or Python or some other version of Java or whatever) but that if you are focusing on comparing things, don't use examples that are so easy to write off. It is not clear to me, as a person experienced with Java but not with Clojure, that the Clojure method deals with many types of character sequences, nor is it clear to me that it's faster or slower than a regex match. If the argument solely lies on lines of code, my mind will think "is he comparing code optimized for performance from one language to code optimized for conciceness from another?" Is that ability to traverse sequences some bona-fide intelligence on the languages part or just a mess of instanceof expressions that really only work for a few expressions?

This just struck me because I've been evaluating a lot of languages lately (Clojure is up soon) and it's frustrating to wade through people talking about languages using examples that are ignorant of all but their own, or contrived solely to make a point. Demonstrate the language in how powerful it is, how efficiently it runs, how gracefully it handles errors, how many lines of code it takes to solve specific problems, not standard utility ones that could easily be tucked into some library somewhere and it doesn't really matter if its 2 lines or 20.


Hrmmm..

I don't know, that is a lot to ask. You yourself state that you can't understand this example in Clojure, which as you admit is almost trivially simple. Again, I'm 99% positive the Clojure as written is exactly equivalent to the full Java versions, so he really couldn't be clearer on this particular example.

It is a hard task in the end as an author, to come up with an example that CAN be understood by someone who likely only knows one of the languages talked about, yet still provide some comparison. I think he deserves some credit in that he does try to make them exactly the same code (probably very similar bytecode even), just expressed in that language.

To your contrived point, I would say running through the elements in a list and operating on them is far from contrived, rather it is one of the most common operations we do day to day. That Java is particularly verbose about that isn't the fault of Clojure.

To keep this constructive, what piece of Java code / functionality would you use as an example?


Well, it's asking more, but not necessary a lot, especially in term of the thorough job this particular author did in the remainder of the article.

Running through elements in a list is obviously common, but that's not what was done here, he was checking for a particular validation that just so happens to have been easy in Clojure and messy in Java.

Here's an example that I think is a little more realistic, but not so complicated that it requires a lot of work. It doesn't use anything outside of the standard SDK, and yet it illustrates some i/o, data structures, error handling and return values, and useful logging, all in a pretty small amount of code that you could definitely see in a real project:

https://gist.github.com/975368

This is clearly not Java at it's best, the finally block shows how some basic scoping can get verbose, and the nested try/catch isn't great, but it's tough to improve it without refactoring some of the messy bits out, which would really only help with organization, not verbosity.

26 lines. I expect Clojure or most other languages to do it in fewer lines, but I'd be surprised if they can do it in half and still account for the requirements. My overall point here is that you could probably read a file into a list in some languages in a couple of lines, but when you start adding some reality in, they pack on some pounds.

If someone wants to prove me wrong, please do, you might have a convert :)


Here's my stab at it:

https://gist.github.com/976020

I think the biggest advantage of Clojure over Java for code like this is how easy it is to use lazy sequences and higher-order functions.

In this example there isn't much to be gained in the error-handling and logging; however, if there were other similar functions it might be easier to factor out some of the common patterns in Clojure, since functions like the predicate to ignore a line can be easily passed through. Or, for example, you could write a custom version of with-open or line-seq to do the desired logging behind-the-scenes.


Thanks, that's a very helpful illustration and gives a good idea of where it ends up. It's actually more readable than most of the other examples I've seen which I think err on the side of being too simple or too clever.


It will be shorter, maybe not by half. But keep in mind that the "reality" you are talking about is in some aspects an artifact of the language in which the rest of the software system has been constructed. For instance, some edge cases (such as special treatment of nulls, some exceptions) happen to be less important in Clojure and other "Java.next()" languages.

I do hope someone will post an equivalent Clojure program. Unfortunately, my knowledge of Java is limited, and I have not touched Clojure in many months now, so cannot do it myself. Sorry.


I know, i like clojure. Diving into it for a while now. :)


I agree with the other comments which mentioned that Clojure is more flexible here (works with any seq, not just character strings). A similar Java code would have to accept an enumerable parameter (not sure what it is in java, something like IEnumerable in .NET) and the loop would change accordingly. The loop-less regex version would not work (it might with the input converted to a string, which may or may not involve a explicit loop, depending on what is available in java ecosystem).


The Person example also seems overly verbose. Does the equivalent Clojure example have private attributes name and age? It looks like the author is accessing the name and age attribute directly instead of using getter methods as defined in the Java version.

    class Person {
        String name;
        int age;
        Person(String name, int age) {this.name = name; this.age = age}
    }
This is still more verbose than the (defrecord person [name age]) but still, I agree with the dodgy examples used.

Disclaimer : I don't know Clojure or similar and therefore I could be missing something. I did a quick search for defining private attributes in Clojure but didn't find any good hits.


There are definitely some in the Java community who would view public properties as incorrect code. For example, it was not accepted at the large corporation where I used to work, nor is it allowed in my wife's Intro to Java class. There are some cultural language specifications as well as the technical ones.


It doesn't usually make any sense to speak of getter methods in Clojure. Records are immutable after construction.


I agree with your point, but you might be surprised how many people would write the more verbose version simply because they don't feel comfortable with regular expressions. Or how many wouldn't even think of using a regex for this.


Or who might need this to work for languages with characters outside the A-Z range...


\w instead of a character range might take care of that.


I'm with you. The author doesn't appear to be very experienced in any of these languages. He just seems to be posting his own naive interpretations.

That said, I'm really excited about Clojure. I got back into programming via Ruby and now Clojure is getting me back into data structures.


I'm annoyed and irritated by non-stop off the topic comments. Apart from the fact that the regular expression solution will be several times slower (and not equivalent to the original problem), regular expressions are a DSL of their own right and Java can claim no credit for their conciseness. When you're writing an overview article you cannot go into lengthy examples. These small out of context snippets just serve to illustrate the general idea. I wouldn't dwell too much on them :-)


I appreciate that you can't write entire projects to compare, and for your article overall, just asking you (and other authors who write similar articles) to give people familiar with the incumbent a bit more to chew on.

Nobody writes code to see if a string has an uppercase character, that code was written long ago. Regex may be something that Java imported, but it's right there in the JDK so I have no reason not to use it. How do the two languages look when you're validating email addresses against a whitelist of domains from a config file or URLs for parameters based on some business rules? How do you handle an I/O error when you're halway through writing a file? These are the small problems we know we'll have to do over and over again, but not be able to stash in a standard library (or use someone else's). Pointing out that JavaBeans are silly doesn't hold much weight either, I use lombok :)


How is my comment off-topic? I've read the article author's other work and my general impression is that he's inexperienced with all of these languages but that isn't stopping him from writing ill-researched tracts with poor examples.

I've seen MUCH better writing here on HN.

But hey, downvote away.


Oh, right. bozhidar's account exists solely to post links to this kind of batsov.com's blog.


I've been coding Java/Python for 10+ years (C/C++ before that), and recently started on Clojure a few months ago. I'm having trouble grokking Clojure since I'm not a Lisp guy, but after stumbling around like a blind rat I find that Clojure's syntax is quite extraordinary. I hope Clojure will become more mainstream as more people use them, and creates more tutorials for a layman like me.


I feel like when people are comparing languages, they are exaggerating.. For instance:

  public boolean hasUpperCase(String word) {
      if (word == null) {
          return false;
      }
      int len = word.length();
      for (int i = 0; i < len; i++) {
          if (Character.isUpperCase(word.charAt(i))) {
              return true;
          }
      }
      return false;
  }

  or

  public boolean hasUpperCase(String word) {
      if (null != word)
          return any(charactersOf(word), new Predicate() {
                  public boolean apply(Character c) {
                      return isUpperCase(c);
                  }
              })
          else
              return false;
  }


Small inconveniences add up fast. For example https://github.com/clojure/core.logic/blob/master/src/main/c.... It's a 1000 lines of Clojure, I strongly doubt that this could be implemented in anything less than 5000 lines of Java split across 10 files.


I've been playing a lot with Kawa (Scheme) for Java, it's rather nice.


At this point, Clojure is the it Lisp on the JVM.


What are the cool things we should know about Kawa?


One cool thing is that the Google Android App Inventor is built out of Kawa. (http://en.wikipedia.org/wiki/Google_App_Inventor)

I believe Kawa is also currently a bit friendlier on Android as Clojure has some overhead. (More at http://dev.clojure.org/display/design/Android+Support)




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

Search: