Hacker News new | past | comments | ask | show | jobs | submit login
What is Clojure good at? (cleancode.se)
49 points by bobobjorn on Oct 18, 2010 | hide | past | favorite | 31 comments



Clojure is fine, but just to play devil's advocate:

First of all, the repl is a killer. This is not something that is unique to clojure, but that doesnt make it less good. Beeing able to try out new api’s and fumble forward is great.

You can do this with Java and every other language. There's even a C++ REPL floating around out there. The real difference between Clojure (and CL, and Haskell) is that there is a REPL culture -- library designers use a REPL, so using one to play with their library feels natural. Trying using a C++ or Java REPL and you'll find that... it's not so nice.

Second, there is no compile time. It is not that the compile time is short, it doesnt [sic] exist.

Not correct. Fast compile time is always nice, and it's great that you don't notice. But this statement is simply not technically correct.

Third, functions without state and side effects are alot [sic] easier to test then [sic] java code. Yes you can still create hard to test code, but it is harder.

But of course, nothing in Java forces you to use state and side effects. It's a culture problem, again; but if you use Java libraries from within Clojure, you are going to run into those same culture problems.

Fourth, you can abstract away more code.

This is true. Java is missing something like Perl's "roles" that allow you to have code in interfaces. Imagine an "Equal" interface that defines equals and not equals. In Perl, you can provide a default implementation for not equals in terms of equals in Equals. In Java, well, I hope you like cut-n-paste or messy delegation. (I have never seen a Java programmer do this, but here's the pattern. Create a NotEquals class that has one parameter, a class that does the Equals interface. Implement not equals by delegating to equals in the object that was passed in. Now you have a class that can do equals and not equals; so create an instance and delegate YourClass.not_equals to NotEquals.not_equals. Now you can get not_equals without cut-n-paste. This is too hard for most people to figure out, it seems.)

With a higher-level syntax, you can add Roles later, or automate the delegation, so you can pick which design to use based on which one makes the most sense, instead of which one means you have less boilerplate to cut-n-paste. And that's the joy of high-level languages; you can write good code without wearing out your fingers.


But of course, nothing in Java forces you to use state and side effects.

Just because you aren't forced doesn't mean the language doesn't encourage particular styles based on the tools available.

If a library is designed with the "create object, set initial properties, call methods that alter the properties, retrieve the new properties" paradigm in mind where everything has its own custom structure, then wrapping the whole process in a function that returns standard abstract data structures (lists, hashes, sets) is going to be extra work. Valuable work, perhaps, but many programmers are not going to bother. On the other hand, if the library is designed with "pass arguments, return abstract data structure" then writing other functions in a similar manner will be natural.


But of course, nothing in Java forces you to use state and side effects. It's a culture problem, again; but if you use Java libraries from within Clojure, you are going to run into those same culture problems.

This is a known problem. There is an in development feature called Pods that will allow you to use nasty mutable Java libraries as well write your own nasty Clojure mutable code (perhaps for performance reasons) w/o destroying concurrency guarantees. I'm looking forward to see how it shakes out.


> w/o destroying concurrency guarantees

Pods will provide more than that since you can provide concurrency guarantees with locks only. However, Pods should additionally help with:

1. Separation of mutation logic and value policy with the Pod handling the underlying juggling 2. Lock order acquisition guarantees

There is more to it than this, but stay tuned for Rich's upcoming talk at the Clojure Conj. :-)


I meant to write "w/o destroying concurrency guarantees and w/o adding undue complexity". With Pods you don't need explicit locking from what I understand, right?


> With Pods you don't need explicit locking from what I understand, right?

That is my understanding yes.


Now I am beginning to understand why people like Clojure so much. Instead of calling them "state threads", whose Google results turn up academic papers from the early 90s, they call them "pods", whose Google results have nothing to do with programming.


Having a short name for commonly used structures aids readability. e.g.

    (ref {})
    (atom [])
Instead of:

    (software-transactional-memory-reference {})
    (atomically-mutable-reference [])


I can't really tell if you're being sarcastic or not


In one company, where Scala is used, they decided to refer to Monads as "comprehensibles" (due to Scala's "for comprehensions", which can be used like Haskell's do statement) in their standard library and documentation: lets everybody use Monads without the knee-jerk reaction of "isn't it some esoteric category thing that Haskell people have to use to do I/O".


There is not really alot of documentation yet so you want find any even if you tree more words.


> This is true. Java is missing something like Perl's "roles" that allow you to have code in interfaces. Imagine an "Equal" interface that defines equals and not equals. In Perl, you can provide a default implementation for not equals in terms of equals in Equals. In Java, well, I hope you like cut-n-paste or messy delegation

Scala has this feature too, as "traits". Under the cover, it does exactly what you describe with the JVM bytecode.

Of course, you can "achieve" the same feature with IntelliJ Code->Generate>Generate Delegators. You can use Yasnippets with emacs to do this and I am sure Eclipse has this feature too. While using an IDE can save the tedium of cut and paste ), it can't save the tedium of reading this code and understanding what it does (especially if you're manually mixing in multiple traits this ways).

Many good Java people are very receptive to Scala, but some tell me "my IDE can generate this, why do I need it?". They miss the point of abstraction.

Of course, that doesn't even cover abstractions that the IDE doesn't have: like pattern matching, or typeclass like abstractions with implicits, optional lazy evaluation (you can make a class called "public class LazyRef<T> { T force() { ... }}", but this is something your IDE doesn't have a "refactor button for").

Java's community has been to codify abstractions not present in Java using "design patterns" e.g., visitor pattern for multiple dispatch, delegates for traits/MI. Of course, if you want these features now you can just use other JVM languages (Clojure for MD, Scala for traits). What's interesting is that Microsoft/.NET team has been integrating these features into their "blub for the masses" (C#) e.g., Monads in LINQ despite also support distinctively non-blub languages (F#, funding Scala on the CLR).


'I of course started rambling about how nice it is for concurrent applications, that you can make a map statement run in multiple threads by simply changing “map” to “pmap”.'[sic apparent typos]

While this is sort of true, that is not true concurrency, it is parallelism. It seems that a lot of production Clojure code will end up relying on Java code at a low level. (That was at least my experience in getting things performant).

When you do this, it seems to me that you lose some of the safe parallelism guarantees. Immutable data structures with 'pmap' are great, as long as you didn't use a mutable Java array somewhere... (This is reasonable, you have enough rope to hang yourself). The takeaway is not 'instant concurrency', it simply isn't true. The takeaway 'great built in support for certain types of concurrency' would be better.

I think the 'fourth' point, Lisp is great at syntax abstraction, has always been true, and is a great point to make in favor of any lisp. If you know what you are doing, it is possible to write some extremely pretty, declarative code. (But again, there's no accounting for taste, and you have enough rope to hang yourself).

Lisp: enough rope to hang yourself, or write awesome code. Or (more likely) do both.


I would honestly answer "It's good at removing complexity from your programs". People adopted OO because it eliminated certain forms of incidental complexity from their applications. People adopted GCed languages because it eliminated a particular serious form of incidental complexity for many applications.

Clojure is good at taking the elements of OO that are sound and ruthlessly removing the ones that introduce complexity. Especially those problems which cause incidental complexity to explode under concurrency.


I sympathize with the author!

I had the same experience a couple of nights ago -- a corporate-programmer-turned-manager friend (highly competent, Formally Trained in Dimensional Modelling and Analysis using the Microsoft stack, etc) was asking me, with some confusion, why I'd use a scripting language or any other 'out there' language.

My first answers were, really, only good for someone who already knows what the language features being described are. CL has macros, Ruby is dynamic and the practice of using blocks ... what are those features, and what do they buy you?

I could tell that my explanations were being parsed -- he's a very smart guy -- but the parse tree was just being cached, with timeouts that would expire long before compilation could take place.

So, I changed tactics -- like the author of this article.

"I like these languages because, look! I can type THIS instead of THIS!"

I kick myself now for not showing him some lisp, but I was working on a ruby+sinatra app on my server in emacs in screen, so I brought up a repl and showed him how few lines of code it took to make a class, and iterate through a list, etc.

What stuck was showing the actual code handling signups in the (sinatra, so, from-scratch) web app -- "look, 2 functions, 6 lines apiece, with error handling and everything."

My friend has to manage programmers now, so he knows that (despite the existence of libraries and frameworks and so forth) if you ask for someone to make you an interface that does X, it's rare that you can get a 5-minute turnaround. The productivity arguments carried the day(1).

"Look, I get to type less code than you, by far, every day!(2)" < -- is a quick way to get past whatever mental blocks the person has to considering your language of choice.

And it sure ought to be easy to demonstrate if they're coming from C# or Java, or what are you wasting your time on? ;)

-----

(1) Unfortunately, he spent the rest of the night trying to pitch me on a startup project of his own that he wanted help "implementing," because the "lion's share" of the work, the data model, was done. But I digress.

(2) But with Ruby, it's easier to add "and look how easy it is to read!" regardless of the background of your listener. Languages like Lisp, that involve a "scary" new lack of syntax, well ... still, side-by-side demonstrations like the one in the article are a good way to pique a practical curiosity.


The two examples he gives could be done easily in any programming language that supports higher order functions (ie. functions which take a function as an input argument). To a lesser extent he requires lambdas in the with-memcached example, but it isn't 100% necessary (could have just written the some-heavy-code part in a named function).

None of this is very novel or unique to clojure. Clojure (and other lisps) do have some nice syntax sugar to make writing this type of code easily, which is nice.


First of all, the repl is a killer. This is not something that is unique to clojure, but that doesnt make it less good. Beeing able to try out new api’s and fumble forward is great.

Spell checker, man!

I don't understand all the REPL love. To me it's great for goofing around with something you don't understand (like clojure itself, when I started). But where is the option to save the REPL state? When I'm actually working on a project it's back to the old edit-compile-run cycle, because otherwise I lose my work. Granted, the compile is pretty quick, but that's true when I'm writing java code in eclipse.


How useful the REPL is depends on the language. Some languages have a much richer REPL related toolset than others. I'm guessing your experience with REPLs has been with a language such as Python or Ruby which doesn't have a very rich toolset. I agree that in these languages the REPL is mostly useful for trying stuff out. However, if you try some thing like Lisp with SLIME then you will see a very different experience. In SLIME, the REPL lets you interact with your running application. This lets you query/modify the runtime state. This, combined with the ability to hot swap your code, gives you nearly instant feedback for anything you develop.

SLIME gives you some ability to save REPL state in that you can start a REPL with all of your code already loaded (check out the Clojure bulid tool Leiningen and the command 'lein swank'). If you want to save the complete state of a running application you could check out one of the versions of Lisp with image based persistence or Smalltalk.



If I wanted to use other versions of Lisp, I would be using them. For my environment (lots of legacy java code) clojure is the right language, and I can't reasonably change to Haskell or Scheme (or whatever) because working from files is a little bit inconvenient.

My point was I hear people gushing about the REPL, but I don't find it that useful because of this one deficiency. It makes me wonder if I'm missing something.


I agree, that would be great. But generally you'd just leave the REPL running in a terminal window for as long as the machine was up (in a screen session perhaps).


So... if the machine goes down, I need to rewrite my code? And how do I distribute it?


An excellent question. I've just asked it on Stack Overflow: http://stackoverflow.com/questions/3966925/saving-my-running...


I spent the weekend writing a scraper in Clojure using the HtmlUnit Java library. As much as possible I aimed for purely functional, side effect free code, but as with most Java libraries, using the HtmlUnit API forced me to write functions dealing with mutable, stateful Java objects.

I would have preferred to write everything in a purely functional style because I agree that it makes testing and reasoning about your code so much easier. But looking back, the ability to dip into Java (and use the great HtmlUnit library) was a big win in terms of accomplishing the task at hand.


The main problem I had when I was learning Clojure was definitely finding good, up-to-date documentation about it. There were many times when I'd try an example that I had found online, only to find out that something had changed in Clojure 1.2 since the article was written, or that it wasn't documented well.

For example: Using command line arguments is not clearly documented. The best resource that I found was this Stack Overflow answer http://stackoverflow.com/questions/1341154/building-a-clojur...

Another problem that I've had with Clojure, is that because programs can be written so concisely, it's very easy to get lost reading other people's code when you're first starting out. Things like the "->>" macro and the "#" shorthand confused me when I was trying to learn Clojure because symbols aren't very descriptive, and they're very hard to search for.


The point about functional languages changing your coding style really is true. After playing around with lift in a university course i went ahead and implemented the exact same cache access pattern you have described in a .net application (using lambda in c#):

var data = Caching.GetOrAdd(cacheKey, () => doWorkAndReturnData);


(time (my-function))

is a pretty horrible example because i can instrument my java code with a single annotation, which is far less brittle.


but you have to use annotation and they suck. :)


Why is the time macro brittle?


Just skimming the headlines - it sounds as if Clojure is good at Java. That's it? I hate Java...

Still interested in Clojure, though.


Im not saying that it just is good at java. But talking to java developers, they often ask why they should look at clojure. And for them, i think that spin makes sense.




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

Search: