Hacker News new | past | comments | ask | show | jobs | submit login
Why Clojure? (thecleancoder.blogspot.com)
161 points by DanielRibeiro on Aug 18, 2010 | hide | past | favorite | 88 comments



You know, I really want to like Clojure, but so far I'm finding it a bit of a slog, for the following reasons:

1. stack traces are very hard to decipher

2. ubiquitous laziness can lead to some really subtle bugs

3. the syntax can be extremely obtuse, moreso than other lisps

4. the basic ADT is just a keyed map, which provides a lot of flexibility but also can make data modeling very opaque, even compared to other dynamic languages

5. immutable and recursive as a default takes a lot of getting used to. things I could dash out in minutes in ruby take me hours sometimes

6. other languages have caught up enough that Lisp macros are not as game changing as they might have been ten years ago

I embarked on a project recently to explore machine learning algorithms, implementing them in both Scala and Clojure at the same time. I expected this to be an easy win for Clojure but, to my surprise, I'm much more productive in Scala, my code has fewer bugs, and I can refactor it much more aggressively. I realize that this isn't an either/or thing and it may be entirely my shortcomings at play here, but my instinct is that although Clojure is a superb lisp, it's still a lisp, and it's going to be a niche that most programmers won't choose to occupy.


1. Fair point

2. Mutable state can lead to really subtle bugs

3. Sure but no more than Haskell or any other terse FP lang, Scala included

4. defrecord works wonders. You get all the flexibility of a map with the context of an actual type

5. It takes me hours to do things in Ruby that I could dash out in Clojure.

6. Right.

Quite a few people in the Clojure have come from Scala. So everyone is entitled to their own opinion.


2. Mutable state can lead to really subtle bugs

Absolutely, but these aren't ends of the same stick. You can have immutable but eager, which is simpler to model mentally a lot of the time, at least for me. Simon Peyton-Jones has said that perhaps Haskell should have made eager evaluation the default and I'm inclined to agree that lazy evaluation is very powerful but maybe not a good default.

3. Sure but no more than Haskell or any other terse FP lang, Scala included

True, although the baseline for a lot of Clojure code is higher, IMO.

4. defrecord works wonders

Maybe so. I haven't really gotten my head entirely around the options for datatypes in CLJ yet.

5. It takes me hours to do things in Ruby that I could dash out in Clojure.

It's true. Clojure makes some things that are hard to do in other languages much easier. Programmers are different, so we shouldn't expect one set of tools to suit us all equally well.


Clojure makes some things that are hard to do in other languages much easier.

I once heard it put that "Functional programming makes all problems equally hard." When taken figuratively, it does make a lot of sense: the things that are hard to do functionally tend to be things that are easy to do procedurally (e.g. UI stuff -- the code obviously, not the design).


I don't think you'll find that UI programming in Clojure is all that difficult. Here's the beginning of a relatively involved RTS game in Clojure: http://groups.google.com/group/clojure/browse_thread/thread/....


"2. ubiquitous laziness can lead to some really subtle bugs"

I find myself implementing Iterable wrappers more often in Java now, largely due to the influence of Clojure lazy seqs. Then I can use the abbreviated for loop syntax, for example, without creating a ridiculously large ArrayList in memory.

I know that the Iterator pattern has been around for a long time, but I never realized how broadly it could be applied to use high level iteration constructs while keeping a minimal memory footprint, before I got used to Clojure.

So Clojure has made me a fan of lazy evaluation, even in Java.


It is also very easy in Python:

Use generator list comprehensions:

    (x for x in xs)
instead of building the full list:

    [x for x in xs]
Use generators instead of plain functions when you can.

Learn to use itertools module: http://docs.python.org/library/itertools.html

I use these often:

    * chain()
    * imap()
    * groupby()
    * takewhile()
    * tee()
    * izip()
    * izip_longest()
    * permutations()
You can make you code quite "lazy" if you practice enough, and eventually it just becomes a habit. Whenever you do anything you start asking yourself : "can I do this in a lazy way?"


Iterators in Python are slightly different from lazy lists in languages like Haskell and Clojure. In Python, iterators are stateful; calling next() moves the iterator on, discarding the previous head. In Clojure, a seq has no state, and the head is not automatically discarded when the next item is accessed.


Good point. Even if you don't wind up using Clojure for real work there are certainly things to be learned from it.


Regarding item 1, one of my friends is a professor and tries to fund a student to help with making Clojure stack traces more understandable. I will not say who that friend is and what university or college s/he is in (s/he has not authorized me to say this), but if anyone thinks they can help with funding I will contact the said friend. Thanks!


> 2. ubiquitous laziness can lead to some really subtle bugs

After the recent compiler change to prevent accidental head retention of lazy sequences, I don't remember having faced any subtle issue with laziness. Can you give me an example of what bit you?


I don't have a concrete example at hand but the pattern is this:

1. program crashes with cryptic stack trace

2. add a println to look at the data

3. program crashes again, but can't print anything because lazy evaluation of something in the data structure triggers a new, different crash

So you've got an error you don't understand and you can't even look at the data you're working with so you start the hunt & peck procedure of trying to find the last point at which your data was sane so you can work back from there. Add dynamic typing to the mix and things get really ugly. I spent an hour tearing my hair out the other day when I inadvertently reversed the arguments to a function but didn't get an error until three function calls later because the types were duck-compatible that far.


OT: What ML algorithms have you implemented? How has Scala helped/hindered this? I'm considering doing the same.


You can see what I've done here: http://github.com/cageface/brains

So far I've implemented a neural net, a naive bayes classifer, a decision tree builder, and a couple of clustering algorithms. So far Scala has proven to be an excellent vehicle for this. People say that static languages are better for big teams but I'm finding the type system to be a huge help in solo coding. It makes it so much easier to pinpoint stupid bugs, forces me to think more carefully about the architecture of my code, and makes me much more confident about constant and aggressive refactoring. Also, a lot of ML algorithms look a lot less scary in a concise, high level language like Scala.

The main hurdle has been coming to terms with the Scala collections library. It's a fantastic package, but there's a lot there and it takes some time to learn how the different collection types work and how they interface with the type system.

TL;DR - I'm not sure I could have picked a better language for this.


Î really love clojure, but this article is not a good plaidoyer, for a lots of reasons, the first and most pregnant being the ability of the author to vastly traverse subject he is probably not an expert about.

> Functional programming, of the kind shown in SICP, is a way to write code that does not manage the state of variables, and could therefore be partitioned to run in parallel on as many processors as you like

This is kind of true, but totally misleading, because very few tasks are actually easily paralelizable this way. When you stumble on one, you know it, and replacing map with pmap certainly is a joyful exercise, but this kind of parallelization are easy to do in any languages. The hard parts are those when you need to access shared state across threads. The facilities that clojure provide in this respect, while innovative and enlightening , have benefits that largely remain to be proven.

> languages like F# and Scala which have a complexity and "quirkiness" reminiscent of C++

Erm, sorry, what, F# reminiscent of C++, did you even try to read some code written in F# ? F# has one of the tersest syntax you're likely to find in any language. As an ML descendant it does already have a very clean syntax, and the addition of significant whitespace only adds to that.

> You can write Clojure code that derives from Java classes and overrides Java methods. In short, if you can do it in Java, you can do it in Clojure.

Yeah sure, but in practice, most people that says that didn't ever had to really handle a non trivial project with java interaction in it. I did. And frankly it can get ugly. It's good to have it, but ideally, all should be encapsulated in idiomatic clojure wrappers. While this is not that hard because java interaction is very well thought, it's a long and boring process, and when you're in the middle of a project and just needs some bit of functionnality that isn't in a clojure lib (and trust me there are still a lot of those), you tend to just use the java interop in place. And then you get back and read your code, and you cry.

In short, this is just a rehashing of what you usually hear about clojure, but with no insight of real use. My (uninformed) guess, is that the author probably didn't use clojure a lot, and frankly, this kind of stereotyped talk gets old.


Your response is more misleading than the article:

> but this kind of parallelization are easy to do in any languages.

Not if you have a GIL. Even then the languages without that limitation do not make what pmap does ... easy.

> [STM] have benefits that largely remain to be proven.

I always find this statement to be misleading. STM has been shown to greatly benefit the correctness of concurrent programs, you're managing that less by hand. The only thing that seems to be unproven is whether the performance tradeoff is worth it. It'd be nice to see some numbers that show what you lose in X% of the performance vs removing X% the amount of locking code.

> [Java interop] And frankly it can get ugly. It's good to have it, but ...

Yet far less tedious and simpler than writing a C module, or a C library binding. If you're comparing against that, Clojure's Java interop story is damn heavenly.

> In short, this is just a rehashing of what you usually hear about clojure, but with no insight of real use ...

Perhaps for you and I. But there are a lot of people in this big wide world. Uncle Bob taps into a community that's quite different from the language enthusiast crowd of HN.

Your viewpoint more myopic than the OP. Yet you get upvotes. Oh well.


>Your response is more misleading than the article

Well maybe, but i feel like you are purposedly misunderstanding me. This was not a rant about clojure, but about the article, yet you take my quotes out of their original context and make it seem like so.

> Not if you have a GIL.

Ok, fair point. But that's not the question. The OP was claiming functionnal programming made parallelization a breeze, which is a very stereotypical position you hear a lot lately. Well, the kind of parallelization which is made a breeze by FP is also the kind that is very easy, not in any language, but in any language claiming to have parallelization features, cf. delirium's answer.

> Yet far less tedious and simpler than writing a C module, or a C library binding

Okay, i was merely reacting to the claim that "anything you can do in java you can do in clojure". Which, for taking your comparison, would be like saying "anything you can do in C you can do in Python". Okay not exactly like that, but the fact remains. The impression the OP gives in this regard is that , since there is this java interop, the library problem is solved. Well no, it is not, and very far from it. If this kind of bullshit marketing talk went away, you'd see less newbies complaining in #clojure or on the group about how it's hard to do X.

> Uncle Bob taps into a community that's quite different from the language enthusiast crowd of HN.

But i'm commenting on HN, not on his blog. This is for a reason. I'm judging the interrest of the article relatively to the crowd here.

On a side note, i did not even notice it was Uncle Bob. If i had, my rant would have been even worse, or i would not had written it maybe. It does indeed exhibit the same emptyness i feel from every of his articles. Maybe it's a good article, and i'm overly critical, and i should probably try and write my own article about why i love clojure, to realize it's not necessarily that easy. But i have a very bad reaction to marketing talk.


>> but this kind of parallelization are easy to do in any languages.

> Not if you have a GIL. Even then the languages without that limitation do not make what pmap does ... easy.

Sure, but it is easy in a lot of not-very-functional languages. The kind of parallelization that you can do by simply replacing a map with a pmap can usually be done in one line of OpenMP in C, C++, or Fortran, telling it to run the loop iterations in parallel.


You emphasize two points that I would also like to add to.

1) Functional languages makes parallel programming magically easy.

This is something that is misleading as you point out. Functional languages (where you have not used mutable state) do make it easy to convert you algorithm to run on multiple cores but that doesn't translate to an algorithm that scales well with number of cores. The problem of parallelization is not one of syntax or even semantics, it is an algorithmic one. You need to have in mind the problem and how to make it scale. Just cause you didn't use state doesn't mean that the algorithm that you wrote automatically takes full advantage of parallelism to solve a problem.

What functional programming does is allow you to more easily express the parallel algorithm when you have it as well as giving you more options in how to do so. But for now and maybe this is cultural inertia, coming up with parallel algorithms is hard regardless of language. This is a distinction that is often glossed over probably because those who are aware of it find it so basic as to not worth mentioning.

2) F# and Scala are quirky and complex

I will give a disclaimer and say that I use F# to write machine learning stuff so I am likely extra sensitive to this kind of bashing but..

That statement makes no sense and has no real meaning. Does he mean that the semantics allow undefined behaviours ala C++ or that the syntax is overly verbose or the syntax is just plain confusing? F# syntax is not so far from ML or Haskell or even python. As for undefined behaviour, strongly I doubt that applies. Scala has a formal specification. While F# doesn't have a formal specification it does have a very thorough (good enough) informal one. I am certain that it will not have implementation dependent behaviour and its strong static type system and garbage collected nature precludes the chance for un-trapped errors so the C++ style undefined behaviour can't be the issue.

F# is certainly not verbose and although Scala, being more OOP centric is a bit more wordy, both languages are quite expressive. Terse syntax is meaningless. What matters is how easy and quickly it is to correctly express an algorithm balanced with how easy it is to mentally uncompress it later (on this case F# may even sometimes be too terse! I have taken to commenting and using quite long names for complex algorithms since otherwise it takes a goodly number of minutes to decompress - especially when the code is littered with single letter variable names that the mathematics like syntaxes subconsciously promote). I will admit that where F# is clunky is the number of ways you can cast and it is a fairly big language with Active Patterns, Workflows and Async and Agents on top of the typical functional idioms. But each such feature transfers a lot of expressive power - a fair trade.

To brush aside languages like Scala , F# without having spent much time on them and in such an ambiguous manner is simply not fair. It is okay that he chose Clojure (wonderful language) but did not have the time (measured in months - fair enuff) to properly investigate all the choices but to brush them aside so trivially is intellectually dishonest, to be brutally truthful.


To brush aside languages like Scala , F# without having spent much time on them and in such an ambiguous manner is simply not fair.

I made this mistake myself at first. A quick first glance at Scala can be a little intimidating. To somebody unused to static typing it can seem wordy and overly formal. Clojure at first blush seems far more minimal and elegant. To my surprise though, once I got my head around Scala's type system, which is really not that complex in typical practice, I found that Scala is also a language composed of a small number of tightly interlocking parts. As I said earlier, I've been working with them in parallel and I'm very productive in Scala now, much more so than Clojure, despite the fact that Clojure is a smaller language.

I strongly recommend this experiment to people considering both languages: pick a small but non-trivial project, and implement it in both languages in parallel. You'll learn more about both of them and your affinity for each than you will reading blog posts.


I wish someone would take the compiler infrastructure of Go and would make native Clojure with it, with an easy access to C instead of Java. I need a good exit path from things like Java, not something to just ease the pain.


If I recall correctly this was one of the goals of Guile, which is essentially Scheme, arguably cleaner than common lisp.


I won't get into the CL vs. Scheme pissing contests, but if I remember correctly Guile was basically RMS' answer to Tcl, a language he didn't particularly like. And it was more intended to replace Emacs Lisp, while most of the infrastructure would still be written in C. Running on Hurd, of course.


For anyone interest in Guile's history (and it is kind of interesting), it was born in the great Usenet Tcl War http://www.vanderburg.org/OldPages/Tcl/war/

It was intended to be GNU's official extension language, but it has never really caught on. That's sort of a shame, because it's actually a pretty nifty scheme implementation.


That sounds correct, but also embeddable and callable from C. It's unfortunate it didn't succeed in replacing elisp. Emacs would be even more awesome than it is today.

I'd love to see the same thing done with Clojure and Go.


I'd much rather have a proper, powerful Lisp than being forced to settle on alternate soft and hard layers. But the Common Lisp Emacs project never went anywhere.


Have you ever looked into Lua? It's a nice language in its own right (somewhere between Python and Io), and it was designed primarily for use with C.

Chicken Scheme is good, too.


That is a nice idea. Another way to reach the goal of an alternative native (non-JVM) Clojure would be to build something on top of Racket or Gambit-C Scheme.

BTW, my favorite aspect of Clojure is the common "seq" API for lists, vectors, maps, trees, etc.


I think if you would want a native, high-performing Clojure going the LLVM route would be a possible way. But I was specifically mentioning Go, because it's about as minimalitic as you can get right now, while still supporting a sufficiently large feature set (i.e. not as limited as TCC). A lot of that comes from its Plan 9 heritage, of course.

The syntax ain't that grand, though. And it could have a better FFI. So, having that in one package would be really great for some Unix-y programming. Right now, without too much baggage. Basically the lispy answer to Ocaml. And then build some libraries on it that inherently take advantage of Clojure and don't just wrap existing things. Which probably would be similar to the way the node.js infrastructure is growing.

It's a pipe dream, I know. The ability to use all that JVM enterprise hooplah is too important for most users. And its a pragmatic language, after all.


Sounds like you need to follow Rich Hickey's example and create the language you want to program in, as the scratcher for your itch doesn't yet exist.

So, not a pipe dream so much as a lot of work to be done.


Someone pretty much has, it's called chicken scheme.


Did it get a native compiler all of a sudden? I thought it just translates into C.


Why does a native compiler matter? If its compiler emits C code, you're G2G as far as all of your stated goals.


That's what many people would call a compiler, actually. Compiling is all about translating from one language to another.


Great article overall, but it makes the same mistake as many other Clojure overviews: implying that STM is the only means of managing state. Clojure, in fact has four main ways to do state, one of which is a (thread-local) normal variable. Each is appropriate for a different sort of stateful operation; STM is the most complicated.


It is not a great article overall. It is well written, and conveys a lot of enthusiasm, but has no insightfull/new content in it. I've read every clojure news and article for six months, and this just basically rehashes every "I love clojure article" i ever read.


If you've been devouring every bit of Clojure-related writing (which I've also been trying to do), then as swannodette said to you elsewhere on this page, you very well may not be the intended audience :)


It's at least good for a short introduction - a "why you should be interested".


What are some good articles on why X loves Clojure?


I agree with the article, and I have to admit I think Rich Hickey (creator of Clojure) is a brilliant man. If you check into his thoughts on programming you will find some very interesting arguments. His talk on state and identity: http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hic... is awesome, and I can not recommend highly enough.

If you want motivation for learning a lisp (Clojure is one) check out http://www.paulgraham.com/avg.html if you have not already done so. An awesome essay to re-read if you have not done so in a while.

Edit: Fixed Link (thanks atuladhar)


Can you explain the point he is making in "Are we there yet"? I have watched it but I didn't really get a different viewpoint on state and identity. That could be because I already had the same viewpoint, but I doubt that.


The text below is directly lifted from the Clojure site at: http://clojure.org/state The article describes it more clearly and succinctly then I could.

There is another way, and that is to separate identity and state (once again, indirection saves the day in programming). We need to move away from a notion of state as "the content of this memory block" to one of "the value currently associated with this identity". Thus an identity can be in different states at different times, but the state itself doesn't change. That is, an identity is not a state, an identity has a state. Exactly one state at any point in time. And that state is a true value, i.e. it never changes. If an identity appears to change, it is because it becomes associated with different state values over time. This is the Clojure model.


So is he saying that you should not nest refs? i.e. you should not create a vector of refs and assign the vector to a ref. Because now the state itself can change.


Not sure I'll have time to check out Clojure any time soon, but this post convinced me I want to read SICP. Despite hearing great things about it, my impression was always that it was much better for beginners, and wouldn't teach me that much. Sounds like I was wrong.


The entire book is available on their website: http://mitpress.mit.edu/sicp/

Wanted a deadtree version to read at my leisure, but at $70 I think I'll read the HTML version.

EDIT: found a PDF version: http://www.scribd.com/doc/15556326/Structure-and-Interpretat...


There are used copies available on Amazon (and elsewhere too, I'm sure) for about $40. Recycle, guys!


Honest question: does it take less energy to ship the book across the ocean or to print a new one (with a home laser printer and ordinary paper).


The paperback version is under $40 on Amazon.



Thank you very much, was looking for a version for ebook reading.


Bob's Railsconf 2010 talk, "Twenty-Five Zeros", is also worth a watch if you enjoyed this post of his.

http://www.youtube.com/watch?v=mslMLp5bQD0


Excellent. Since picking up (and falling in love with) Clojure a few months ago, I've had multiple "Why Clojure?" discussions with friends and co-workers. This post is short, sweet, and hits all the points I've tried to make (poorly) to others. Saving it now as ammunition for the next "Why Clojure?" discussion :)


I will be forever grateful, that my University taught Scheme/Lisp with this book (and others) as introduction to programming.

While I have never used Lisp or Scheme in my professional life, that set of undergrad courses more or less defined my mindset when it comes to algorithms and their expression and has lasted to the present day (some 20 years later).


Agreed. I went to the University of Minnesota which modeled its curriculum after the MIT CS program. I was saddened to hear they were ditching Scheme for their intro class. I don't see why they felt the need to update to a more "modern" language. It's impossible for any language to ever more directly express the basic concepts of functions and recursion than lisp. Why muddy the waters with syntax?


I guess there's an established trend how one writes about switching to new language: it's usually a long blog post containing facts distilled by others, sometimes mixed with author's wishful thinking. All that because author probably feels a need to justify his decision.

It's normal - we usually want to justify the reasons why we do stuff, esp. when the new way is time consuming. It's also great that the OP is willing to dive into new waters. But such article cannot be taken as an expertise, even for the reason that it's not from OP's experience (yet).


A more complete and detailed answer to this article's question is provided by the free Chapter 1 of Amit Rathore's book-in-progress, Clojure in Action:

http://www.manning.com/rathore/

As Rathore puts it, Clojure's power is drawn from three mutually supportive design decisions: 1) it's a Lisp, 2) it's functional, and 3) it's on the JVM. Read Chapter 1, it's well written.


I've never more than dabbled in the functional programming world. I've been pretty happy professionally in Ruby for the last 5 years. However, if there's one thing that I learned, it's that unit testing is an invaluable sanity check but a poor substitute for mathematical rigor.

I see my trajectory over the next 5 years to involve learning Haskell well enough to know the functional approach for a much wider set of problems, but then falling back to Clojure professionally because, you know, I gotta get shit done.


Unit testing is a poor substitute for strong typing in this context.


and purity.


Anyone have any recommendations for trying Clojure? I'm an Emacs user on Ubuntu and I'd like to try it out but there seems to be quite a lot of choice about how you do things I'm a bit lost.

Any up to date blog posts on setting up Clojure and connecting Slime to it? The ones I've found are always slightly out of date (apparently starting swank from Emacs isn't the preferred way to do it anymore?)


Yeah there are a bunch of different ways to start Clojure with a REPL and/or swank server running. Maven, Leiningen, and cake are all build tools that also do dependency management - great when you're working on a complex project but have some amount of setup overhead for their project descriptors. cljr is a different option that lets you start a REPL anywhere, with no project descriptor, and also takes care of setting up your JVM classpath with any dependencies you've installed via 'cljr install'.

All of these tools encourage running the JVM outside of emacs, rather than as an inferior-lisp, and have some command line option that starts a swank server on a specified port. After that you can just do M-x slime-connect and get hacking as usual =)


Swank Clojure is a server that connects SLIME and Clojure projects. Its github page has instructions on installation and usage, hopefully that will serve as a good starting point:

http://github.com/technomancy/swank-clojure


I recommend the Clojure wiki: http://www.assembla.com/wiki/show/clojure/Getting_Started_wi...

If there is something that's not covered or linked to by that post, please submit a bug report to Swank-Clojure: http://github.com/technomancy/swank-clojure/issues

There is a lot of bad out-of-date documentation out there, but I like to think the official docs are pretty serviceable; if they aren't please help point out how.


> become enamored with a language that has roots that go back to 1957, i.e. Lisp?

Duh. What does that punk think that the "60" in Algol 60 stands for? Y'know, the bondage and discipline languages also have an ancestry that goes pretty far back.

And I'd choose C++ (or Java) over Algol just as I'd choose Clojure over Lisp1.5 any time.


I'm eagerly awaiting the binary release of ClojureCLR.


...any time a Clojure programmer want's to change the state of a variable, they must do so using the same kind of transaction management as they would use for a database.

Not a good thing. The word 'must' is the glaring red flag.


It is a good thing.

And it's as simple as e.g. (dosync (ref-set ref newval)). In fact, if you find yourself doing it a lot (don't, please!) then define yourself a little function (defn rset [r v] (dosync (ref-set r v))).

Must isn't that bad of a word, and it's a Lisp: what kind of boilerplate do you expect to write? I prefer it going on none.

It's definitely worth checking out, and if you need state, and you'll need it rarely if at all, you're better served using the built-in STM and the wonderful functions that allow you to manage it (like add-watch; what a lifesaver in a state-heavy app like a GUI) to handle the mess.


I don't understand you. If you don't like to write boilerplate, why do you appreciate Clojure making mutation verbose?

There are times when the imperative style is more natural.


I don't understand you.

By this I merely meant, "I don't understand what you are saying". Hope that was clear.


One thing others haven't mentioned, you still have the option of creating a normal java object and manipulating it with the standard method accesses. Those aren't forced to be immutable by Clojure. Fortunately doing that makes it fairly obvious what you're doing since it won't look like seq accesses, creating a code smell so you can decide if you're doing it too much as you get better with the language.


unless things have changed dramatically since earlier this year you can have a mutable cell called an 'atom' if you want. it will almost inevitably be a reference to an immutable data structure but it behaves just like any other mutable cell, and does not have to be wrapped in a transaction.

besides, i think even truly single-assignment languages seem to have a place, just ask those crazy erlang guys ;)

the way i read clojure's philosophy around state is: "you can fuck up the concurrency if you want but we're going to make it really hard to do it by mistake".


It's not strictly correct. You define variables all the time via vars. You must in the sense that if you want to do anything over time, since doing it other way is insane.


It's not totally true, either; there are other ways to do state in Clojure. But none of them simply let the programmer, you know, change the state of a variable.


Sure they do. Thread-local vars behave pretty much like regular variables. Atoms also get you uncoordinated state.


OK, thanks for the tip about atoms. I hadn't dug into the var/atom/ref/agent docs. I should have before commenting.

What I was looking for can be done with atoms, and is made more like traditional lisp mutation by defining a "st!" (or maybe "zet!" is a better name).

    (defn st! [atm v]
      (swap! atm (fn [_] v)))
Now making ordinary lisp clojures is reasonable:

    (def fns
      (let [c (atom 0)]
        {:add (fn [x] (st! c (+ @c x))) 
 	:mlt (fn [x] (st! c (* @c x)))}))

    > ((get fns :add) 3)
    3
    > ((get fns :add) 3)
    6
    > ((get fns :mlt) -5)
    -30
Though I sure could do without the calls to 'atom' and @. Referencing and dereferencing is for C programmers. Maybe using swap! directly is better after all.

    (def fns
         (let [c (atom 0)]
           {:add (fn [x] (swap! c #(+ % x)))
    	    :mlt (fn [x] (swap! c #(* % x)))}))


The function you're looking for is 'reset!'


Ah, thank you.

But what I really want it something that doesn't require dereferencing syntax:

    (zet! c (* c x))


If you evaluate your expression on two different threads, the value of c is non-deterministic. On the other hand, if you call

    (swap! c (partial * 2))
on two separate threads, c will always end up 4x the original value.

These limitations exist for a reason.


It's worth noting you could also write:

    (swap! c * 2)


Yeah, I just thought that looked close enough to infix notation to be confusing.


This would work: http://gist.github.com/536715

I'm not sure wether this is good style. Probably not.

Edit: Bah. Fixed an error.


'replace-in-form' is a tree-replace, which in Clojure is done with clojure.walk/postwalk-replace. The clojure.walk package is basically tree-map stuff.

Sadly, as far as I know, you need to roll your own tree-reduce.


Thanks for that. I did look at clojure.walk, I just didn't look close enough.


Also, where did you find reset! ? It's not on the atoms page in the main documents.


only when pg will use it.. =)


Hey, what's wrong? It is so cool and modern Lisp implementation which runs on even more cool and super-efficient JVM (who said Ernang?!), so, why not?! Let's port HN news app to Clojure and see how much users it can handle. Every one know that Jetty is so cool! ^_^


Because it's so clean.


I don't think so I'll take erlang over clojure any way.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: