Hacker News new | past | comments | ask | show | jobs | submit login
Metabase: Why we picked Clojure (medium.com/metabase)
179 points by tlrobinson on Dec 2, 2015 | hide | past | favorite | 183 comments



" However, we had a strong aversion to using Java itself, so the real short list was Scala or Clojure."

Sadly they couldn't articulate any actual problems with Java...

I find it weird that so many developers focus on the "writability" of the language. I mean stuff that java gets criticized for, like verbosity etc. Which I don't even find so bad especially since the newer jdk versions.

But in my opinion the _readability_ of the result is the most important factor. I rather read copy/pasted code than some smart clojure onliner which nobody dares to touch or really understands.

You only write the program once, but you have to read it over and over again when maintaining it and developing new features, and java's maybe a little verbose syntax really helps here, and also the fact that there really is only one way to do a thing (unlike eg. perl).

The tooling, documentation, frameworks, libraries and runtimes are all top notch with java. All other languages seem to have problems in one (or more) of these areas.


After you've been writing it for a while, reading Lisp / Clojure at a glance is not nearly as scary as it seems at first. You get used to it, and that one-liner becomes a paragon of elegant simplicity rather than a strange collection of symbols.

It's just a different mode of scanning than what we've become accustomed to by working in mainly ALGOL-derived languages. With practice, it becomes second nature (and ALGOLs seem needlessly verbose by comparison :)


I've tried unsuccessfully to learn the zen of Lisp.

When you read a Lisp one liner, to me it seems like there are many implicit details you have to hold in your head. "Ok after this parenthesis now you have a list of maps keyed by symbols with string values. Ok go up one parenthesis and now it's transformed to a list of maps with..." Continue unwrapping for 3 levels. Get messaged by coworker. "Ok. Where was I? Fuck."

Rich Hickey said something along the lines of "If a function only deals with mutability in local variables (for performance), it's still a pure function". I prefer reading imperative functions that are still pure in that sense and spell out each transformation line by line to reading highly compact code written in powerful languages. Maybe I'm just dumb.


Why dumb? Perhaps you only need longer time to adapt to the new status... until the new way to structure the code becomes your default way to present functions.


Writing is the key, I wrote in 10 programming languages and lisp still looked alien to me. It was only once I was forced to write out examples in a programming languages class that it started making sense. You really have to try writing in something novel like a lisp dialect to assess whether or not it makes sense!


'Couldn't articulate' vs 'didn't articulate' are two different things.

The tooling, documentation, frameworks, libraries, and runtimes are all there if you want to use them in Clojure, too. So that's not a point in Java's favor, just the JVM's favor.

It is quite possible to write reasonably readable, terse, correct Java programs. I rarely ever see them in the wild though; what I've mostly seen are klugey Spring or JavaEE based monstrosities that are horribly inefficient, and prone to hard to debug concurrency issues.

The average results seen with the language -is- a consideration; while it's hardly scientific (the teams choosing Java tend to be from all walks, and disproportionately from large enterprise teams where no one gets any real say in the matter, that's just the common denominator that management has allowed; the teams choosing, say, Clojure, tend to be smaller and made of good developers who are intellectually curious, and have found themselves in places they can make that decision, so obviously the average for the latter will likely be better), it's still a consideration. If you've written in a wide range of languages, you've -seen- this in yourself; what the language makes easy vs what it makes hard influences how you do things.


Clojure allows me to express thoughts that I wouldn't know how to express in for, while, if, variables and objects.

And on readability, what do you prefer?

  List<Integer> even = new List()
  for(int i = 0; i < 100; i++){
    if((i%2)==0){
      even.add(i);
    }
  }
  return even;
vs.

  (filter even? (range 0 100))


That's not a fair comparison. What about:

IntStream.range(0, 100).filter(i -> i % 2 == 0).collect(Collectors.toList());

Disclaimer: I'm not writing Java nowadays, so this is my quick prototype from a quick google-session, it probably won't compile :D

Edit: looks like I'm second, my google-session took too long :-)

Maybe bonus points for C#: Enumerable.Range(0, 100).Where(i => i % 2 == 0);


True... although that is to me even less readable than the first Java version. The clojure version still wins by a large distance in my book because of its elegance.


That's not bad, but in the clojure version, I have to know/learn three functions: filter, even? and range

In the Java version, I need to know/learn IntStream.range, IntStream.filter, IntStream.collect, the even lambda, and Collectors.toList

Of course, this is comparing one-liners, so doesn't necessarily translate to larger programs. Just pointing out that I don't find this one particular example to be in Javas favour.


> IntStream.range(0, 100).filter(i -> i % 2 == 0).collect(Collectors.toList());

This has been added to java to catchup with functional programming languages.


I know those are functional concepts, but it feels more like trying to catch up with C# (LINQ) to me.


That's better, but Clojure still has less noise, and in my experience that adds up significantly in the long run.


Less noise, true. But the grand-grand-parents' point is that some of that noise provides context, which (arguably) improves readability.


The examples don't support that assertion though.


But what is even?

I am sure functional programming has its natural appealing in areas like recursion, but this example doesn't seem too convincing for winning readability, I can always write this in python

filter(even, range(0, 101)))

where even is a function I wrote.

Please give an example for "Clojure allows me to express thoughts that I wouldn't know how to express in for, while, if, variables and objects." if you can.


Your Python example is functional programming! You're doing exactly the same thing as in Clojure, just not in a Lisp syntax.

Therefore, in your question, you could substitute Clojure with functional programming, and the examples in which FP allows you to express thoughts you wouldn't know how to express in for, while and so on -- are plentiful.

I may not be answering your question, but there's a reason for that: the functional programming aspect isn't Clojure's main selling point. It's the other ideas. Software transactional memory, immutability, its emphasis on state and identity, the protocol system, transients, concurrency, transducers, the list goes on.


You are referring to higher-order function I assume. I don't think that is a property of FPL.

I am definitely not dismissing FPL, I am just arguging about the actual examples given were not strong enough to dismiss Java being hard to read.


The big difference is that Clojure defaults to immutable data structures. While you can chain functions together in Java and Python, you still have to manually keep track of all the references.

The main benefit of modern functional languages is in their use of revisioned data structures. This has same benefits as garbage collection.

Without GC you have to manually track where you allocate and deallocate things, and this is highly error prone. With GC the runtime does this for you, and you can focus on other things.

Same thing applies with mutable and immutable data. With mutable data structures all the changes are done in place, and you have to keep track of everywhere it's used to update it predictably. With immutable data structures you're "copying" the data any time you make a change, and the runtime keeps track of the references for you.


There's nothing preventing you from writing analogous data structures in any language.

No one wants to admit that their language choice is because of the cultural norms in their language community, but if you can hide some complexity behind a language construct, you can hide the same complexity behind a function call. It's just a question of how much of the work required to do that has already been done by the community.

You're just not going to win the "my language can do x more concisely than a function called x in your language!" argument. You're better off with "this is easy and my language and you'd have to write a function to do it in yours."


There's nothing preventing you from writing analogous data structures in any language.

The problem is the language culture and what is idiomatic. I write Python in my day job and while I could certainly write immutable data structures in Python and code in a way that treats data as immutable, it would be going against what is idiomatic in the language, which means that it won't play well with libraries and that my coworkers can still trample over my data without my knowledge. That is, the language gives me no guarantees whatsoever that my immutability is actually respected and that other code somewhere else that I did not write (either application code written by a coworker or library code written by a stranger) respects the immutability.

I find this argument similar to asking why bother writing in any high level language when you can implement the same things in assmembly. Hell, why don't we all just program turing machines directly?

Languages carry a lot more with them than just the features - they carry idioms and culture and community trends. All of these (and even small syntax differences) influence how we think about problems and how we create and structure solutions. They also influence how my code will interact with other peoples (and theirs with mine).


Exactly. But there are also cultures within languages. Just because your language has a clique doesn't mean you have to hang out with them.


True, but how far away you can get or how well you can play with libraries or other code can be limited.


You're right, _you_ can write immutable data structures in any language. But for example, immutable.js and/or mori do practically nothing to fix the mutability problems with javascript, since immutability is not the default.


Higher order functions are _the_ defining property of FPL. Even though a lot of FPL people might want you to believe it's types.

Lambda calculus (the theoretical foundation for all functional languages) was initially untyped.


Arguably, first class higher order function (and immutability) is defining characteristics of functional programming.

Although, I don't think the functional/ imperative is necessary a good axis for categorizing languages. There are potentially more differences between Clojure/Ocaml than Python/Clojure, for example.


They are no longer exclusive to FPLs but higher order functions are definitely a property of FPLs


I'd say the argument for FP in general is not that it allows for the expression of thoughts that are impossible to express with for, while, if, etc., but rather that functional programs are still able to be reasoned about at the point of complexity where imperative programs become completely intractable.

Not an FP expert so I don't know if this is entirely accurate or how it plays out in reality.


even? is a predicate that test if the parameter is an even number, as the name suggests. ? is part of the function name, not a syntax symbol (there's not much syntax in a Lisp, after all). By convention, function name ends with ? are predicates (or Boolean functions) in Clojure.

Any competent Clojure programmer would likely have known most of the Clojure core functions by heart: map, reduce, filter, etc. There are maybe about a hundred of them?

Unlike imperative programming languages, where you use three primitives (assignment, condition and loop) to construct everything, in Clojure, you use these core functions to construct your program. In a sense, the process of learning Clojure is the process of learning the use of core functions. These functions are simply at a higher level of abstraction than the three primitives, hence some people say that Clojure is more expressive, because it's closer to human thoughts than Von Neumann machine specificity.


> Any competent Clojure programmer would likely have known most of the Clojure core functions by heart

I really like Clojure, but I've always found the relatively huge set of core functions to be a major barrier to entry. I always have this nagging feeling of "there's probably a core function I'm missing to make doing this or that easier", and end up spending a lot of time poring over documentation instead of just programming. Maybe that's just how it is, and the answer is to push through it until you know the functions better, but there's something to be said for shipping a language with fewer, simpler, more primitive operations.


It's definitely a taste thing. I found going from the batteries-included Python to the very limited strict C89 ansi-pedantic-standard rather annoying, but at the same time I got a lot of pleasure building a game from scratch in SDL instead of using a full-fledged engine that would have replaced a lot of my code with one-liners or even automagically managed bits in the background. I think a more overkill approach is better economically than being conservative, and in Lisp especially including a ton of common macros like 'or out of the box saves every other programmer from writing their own. I could go the rest of my life without seeing yet another custom <insert data structure here> in C or C++ which has a good implementation natively in almost every other language. I wish JavaScript had more builtin as I'm often in the position that things I take for granted as being there in Python, Java, or Clojure require me to roll my own in JS and that can easily take hundreds of lines of my own code or dependencies depending on what it is. (e.g. anything involving large integers.) I'm greatly thankful for regex support in my daily languages. I remember in a high school programming contest, one guy didn't know Java had regexes (or even what regexes were) while I knew them from using them in PHP to validate form inputs... Needless to say, he never got a fully working parser in the time he had for the problem.

You may find https://github.com/jonase/kibit helpful for some things. Otherwise I'd recommend just dedicating time to exploring the builtins so that you can resist the urge to wander while you should be programming -- there was a very interesting series a few years back some blogger made that covered all of Python's builtin modules, doing it yourself (or maybe there's one now for Clojure) is worthwhile. I also like to visit things like http://www.clojure-toolbox.com/ from time to time to see if there's anything new and exciting in areas of my interest.


From a cognitive point of view, learning a large number of idioms is more manageable for human beings than having to reinvent the mental constructs from primitives every time. This human capacity of dealing with idioms is what called "chunking" in psychology (https://en.wikipedia.org/wiki/Chunking_%28psychology%29)

Human has unlimited memory capacity and fast memory access, whereas human logical processing power is fairly limited and error prone. That's why any real human language has a large vocabulary.


"there's probably a core function I'm missing to make doing this or that easier"

Would you prefer if there wasn't such a function to make it easier? You lose nothing if there isn't (or you don't know about it) and gain if there is. Why is this a barrier to entry?


I would prefer if there were very few un-namespaced functions which are limited to those I'm likely to use on any given line of code, and an intuitive namespace hierarchy for everything else. Basically, I think the core functions should be similar to reserved syntax is in other languages: a limited set of things that form the structure of most code, with more specific stuff imported from elsewhere. Note I'm not saying that the clojure way is "wrong", just that this[0] is a daunting set of information to be expected to "know by heart".

[0]: https://clojure.github.io/clojure/clojure.core-api.html


Ah, I see. I can't disagree with that :)


We were talking about java here. ^^ So python is offering a lot more already with it somewhat first class functions. What would you guess even? does, btw?

But sure, let's make things a bit harder. As below, let's produce a sequence of the form [true false false true true true false false false false ...] of exact length 1000 without computing any more than exactly 1000 elements.

  (take 1000 (mapcat (fn [i] (repeat i (odd? i))) (range)))
Of course the really interesting stuff is hard to do in one liners. But I think one of the more mind bending things is probably core.async. Go style coroutines and channels implemented as a library on top of the language. Which allows for concurrent programming inside clojurescript which is compiled to JS and thus inherently single-threaded.

http://swannodette.github.io/2013/08/02/100000-processes/


> (take 1000 (mapcat (fn [i] (repeat i (odd? i))) (range)))

And this example is suppoed to support Clojure's readability?


You do it in a different language that isn't haskel or ml and show me the result, then we can judge.

You'd btw probably write it as.

  (->> (range)
       (map #(repeat % (odd? %)))
       (apply concat)
       (take 1000)
If you want it more readable.


You missed a closing paren :)


It shows that Lisp programmers really don't care about balancing parenthesis, because editors do that for them.


Yeah, I really love the new parinfer stuff :D


whops ^^°


My point was "even" and "filter" can be built by someone, just to be fair about the whole readability. If "even" is already built into the language, then that's a given.

Readability and language understanding are not separable. I for one have a hard time actually understand what the code does above (although I can guess it). But I am not sure how ^ translate into [true false false true true true false false false false ...] sequence because, I thought we were calculating oddity of 1000 numbers.

[1 2 3 4 5 6 7 8 9 10 ...] ==> [T F T T F T F T T T ]

Perhaps the lack of understanding Clojure syntax to comprehend what the code is doing.


Exactly the point. In many other languages, every programmer can write their own "even" and "filter" with their own idiosyncrasy. However, in Clojure, every one uses the same set of high level functions in the standard library, because they are sufficiently rich to express most of the programming concepts, without having to roll your own.

In Clojure, readability is obtained through standardization. Every Clojure programmer instantly knows what the functions in the standard library do. Because there's not much syntax in a Lisp, code is very easy to read once one knows the functions.


> In many other languages, every programmer can write their own "even" and "filter" with their own idiosyncrasy.

"filter" is incredibly useful, and most modern languages (including Java 8) have it built in.

"even?" is sometimes useful, and languages are kind of hit-and-miss about it, but its usually small enough to inline, and the biggest cost of not having it built-in is probably things like people using modulus rather than first-bit testing in Java which, IIRC, produces somewhat slower code for that check.


It's part of the std lib. But yeah you're basically on the same page as me. Java doesn't have the ability to be extended in the ways that clojure or python can.

Python also allows for functional programming to a certain degree, but clojure is a lot more powerful when it comes to language extensions.

Threads as a library (for js environments) for clojure. https://github.com/clojure/core.async

Logic programming for clojure. https://github.com/clojure/core.logic

Declarative UI building, with dataflow semantics. http://reagent-project.github.io

Additionally immutability makes your life a lot easier. Parallel computing in clojure is a no brainer, you just change your map to pmap, and that's it.


Thank you. I will look at the immutability part. I remember taking Racket in school a few years ago and that was a major thing about FPL like Racket / Lisp.

I have always wanted to look at building something again in one of the popular FPLs.


> As below, let's produce a sequence of the form [true false false true true true false false false false ...] of exact length 1000 without computing any more than exactly 1000 elements.

So, again, python:

  [i % 2 == 0 for i in range(1000)]
(If you insist on Java, its fairly straightforward, though not a one-liner AFAIK, using Java 8 generators.)


You misread the question. I didn't mean (map even? (range 1000)), but a sequence of length 1000 where the elements are 1 * true, 2 * false, 3 * true, 4 * false and so on.

To compare it with what your code produces.

[true false false true true true false false false false ...]

[true false true false true false true false true false ...]

Edited for more clarity.


Right, sorry, in python that's something like:

  from itertools import chain, repeat

  chain.from_iterable([repeat(i % 2 != 0, i) for i in range(1,1000)])
In Java 8, I think the solution with streams would be fairly straightforward, something like:

  IntStream.range(1,1001).flatMap(i -> Stream<Boolean>.generate( () -> (x & 1) != 0 ).limit(i))


Doesn't that python code give you a list of 500,500 elements, rather than a list of 1000 elements as originally specified?


In both cases yes, and in both cases because I made the same stupid mistake when both languages have good means to do infinite streams and "take" equivalents. (And that's worse in the Java case, since I used those in the inner function but not the outer one.)

So, I think I need to take a break from comment-box programming for a while; its clearly not working out for me today.


Great Clojure feature:

    user=> (source even?)
    (defn even?
      "Returns true if n is even, throws an exception if n is not an integer"
      {:added "1.0"
       :static true}
       [n] (if (integer? n)
            (zero? (bit-and (clojure.lang.RT/uncheckedLongCast n) 1))
            (throw (IllegalArgumentException. (str "Argument must be an integer: " n)))))


Even as someone who prefers a functional style, that quote is pretty unfortunate. Any Turing complete language is going to be able to express the same ideas (with varying degrees of gracefulness, resource usage, ease of debugging, etc).


Of course, dynamically typed languages will win on one liners against Java but in the long run, the absence of types can really bring an entire code base (and project) down, which is why Clojure is hardly used at all and why Java is everywhere.

Besides, Clojure doesn't even win in the one line department against modern JVM languages (e.g. Kotlin).


> which is why Clojure is hardly used at all and why Java is everywhere.

Surely that has nothing to do with Java being 20 years old, and Clojure 1.0 being 6 years old?

> Clojure doesn't even win in the one line department against modern JVM languages (e.g. Kotlin).

Example, please.


> Surely that has nothing to do with Java being 20 years old, and Clojure 1.0 being 6 years old?

Lisp has been around for 50+ years, hasn't really helped its popularity much. But I'm willing to bet with you that in 14 years from now, Clojure will have even fewer users than it has today.

> > Clojure doesn't even win in the one line department against modern JVM languages (e.g. Kotlin).

> Example, please.

Here it is in Kotlin:

    (0 .. 20).filter { it % 2 == 0 }


> Lisp has been around for 50+ years, hasn't really helped its popularity much. But I'm willing to bet with you that in 14 years from now, Clojure will have even fewer users than it has today.

Computer science has too short a history to make any sound arguments from history. However, if you insists on arguing from history, let's look at the long term trend. Would you agree that the trend of programming language is progressing to higher and higher level of abstraction and further and further away from the machine details? From machine code, to assembly language, to C, to Java, and now FP languages, this trend of mainstream programming language evolution is very clear. In this sense, if you take Lisp as a pioneer of function programming, it was simply ahead of its time.

If you are implying that Lisp syntax is somehow an impediment to its popularity. That may well be. It's a personal preference issue. Some people like more syntax in their language, some people like less. If you are in the later camp, Lisp is surely very attractive. For example, in your kotlin one-liner, there are just too much syntax to understand "()", "..", ".", "{}", etc. Life is too short, I just don't want to learn all these idiosyncrasy invented by someone just for the sake of being different. I wished I had knew Lisp sooner, certainly not having programmed for 20 years.

I am sure you will lose that bet.


Even if your assumption about the trend being to higher levels of abstraction (which is highly questionable) is correct, this doesn't give any credence to the prediction that it will make Lisp finally popular. Yes, Lisp is a 4GL but even if everyone is rushing toward 4GL, there are plenty to choose from and it's not clear that they will flock toward Lisp.

Even if CS is still pretty recent (50+ years), there is still something to be said about a language that never managed to become mainstream in that period. Whether Lisp was ahead of its time is irrelevant. The question is: will there ever be a time where Lisp will be ripe for its time?

I think that window closed a while ago. It's not about the parentheses or its FP side, it's just that the world is moving toward statically typed languages with sophisticated type inference, and it's a league that Lisp simply doesn't belong to.


> it's just that the world is moving toward statically typed languages with sophisticated type inference

The billion lines of Javascript, Python, Ruby and R would like to differ.

Each of the first three has probably more users than all languages (ML family, haskell, scala) with an advanced type system combined.

I love dependent typing, I truly do. But the right tool for the job, and a formally verified coq program is certainly suited for a rocket, but not for the high iteration and velocity environments most people program in.


> Would you agree that the trend of programming language is progressing to higher and higher level of abstraction and further and further away from the machine details?

That's...less than clear. The extreme high level of abstraction in PLs is consistently increasing, or at least non-decreasing, over time, but the mean/median used in actual programming may not be.


With some minor assumptions, my assertion would be correct. Yours needs a lot of strong assumptions to be mathematically feasible.

Basically, you are saying, when the max of a sequence is increasing, the mean/median stays or even decreases. That would require that the frequencies on the lower end to increase much faster than that on the higher end, which is certainly not the case. For example, the number of C programmers is growing faster than the number of Java programmer today? No one would think that's correct.


I'd say clojure won that one liner.


There will have fewer users according to Google Trend:

https://www.google.com/trends/explore#q=java&cmpt=q&tz=Etc%2...

And Clojure, not many is interested in it:

https://www.google.com/trends/explore#q=clojure&cmpt=q&tz=Et...

Actually I'm on the Clojure side. News comer will always pick a language which is simpler, more expressive, newer. Java is no longer such a language comparing to others.


I still find the clojure version (ever so slightly) simpler. Mostly because this has magic implicit it variable. In real code, this of course wouldn't matter, though.


One hand picked example from someone who prefers closure, which only works because there is a keyword "even".


It's a function in clojure.core, not a keyword. Sorry I didn't have to write a class for that.


Ok you provide a example in java, and I'll bet you a beer that I can write it more concise.


In my experience, the readability of Java is not that great, perhaps due to verbosity. Especially when you want to create a DSL with some type-safety guarantees.

Wrapping a head around a factory that is configured by fluent-like-api, with interfaces scattered around 10 different files usually took me longer than understanding idiomatic clojure.

When I was working in clojure, only thing I was really missing was the type-checking. But when I work in java, i miss the REPL much more, than i missed type-checking in clojure :)


> But when I work in java, i miss the REPL much more,

Both IDEA and Eclipse have had a REPL for a decade, not sure what exactly you were missing(?).


I missed mostly three things:

1. simple data definition. Clojure has EDN, python has nice syntax as well, and javascript has json.

2. pretty print

3. succinct function definition/execution, and standard array of functional tooling (map, filter, reduce, etc)

Maybe the 3rd thing did get better with Java 8?


The small amount of development I've done with Clojure convinced me that you just can't ignore Java - you're going to end up spending time reading Java docs, and debugging Java stacktraces. If you really dislike Java I don't think choosing Clojure is a great option, either (I don't know about Scala).


(Metabase dev here)

> you're going to end up spending time reading Java docs

That's actually a benefit IMHO. If no one has created a nice Clojure library or wrapper for something you want to do it's at least possible to use the massive number of existing Java libraries directly from Clojure.


"Sadly they couldn't articulate any actual problems with Java..."

The first thing they said about Go applies to Java: "Go felt promising but too low level for us to express our query language productively."

You certainly can write that sort of code in Java, as evidenced by the fact that many people have and continue to. But you can get a very big win on that sort of code in a lot of other languages, especially if you're resource-constrained.


How is Java low level. Java is a high level language. What is "low level" about Java? Do you mean statically typed makes it low level? No it doesn't. It makes it safer though. Low level means direct access to hardware and having to deal with network protocols manually and shit like that. None of which you almost ever do or would want to do with Java considering it runs on a cross platform runtime mostly isolated from the actual OS of the host machine.


It means that when it comes to writing code to deal with a query language, which involves having an AST and manipulating it every which way, Java gets creamed by many other languages. Both the Lisp series of languages (which Clojure is in) and the ML series of languages have wildly better stories for dealing with AST manipulation, as in, better enough than Java that it's worth learning those languages just for that, if that's the task you have.

Whether or not Java is a "bad" choice is a definition issue, that it is handily beaten by many other languages is pretty concrete.

I'm not saying this as a Clojure partisan, or a Java-hater, or anything like that. I'm saying it as one who has used enough of the relevant languages to know it's not even close.

For something more concrete, I'm more on the ML side, so work through https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_... and you'll see what I mean, even if you just do the first couple of chapters. For all that Haskell may be weird in some ways, consider what it means for a tutorial of a language to be talking about parsing in the second section. You'd never get that with Java.


Java has a series of control mechanisms that originate from idiomatic assembly. for, while, if, variables.

In clojure for example you rarely use these low level constructs.

What is simpler to read? Which one is more high-/low-level

  List<Integer> even = new List()
  for(int i = 0; i < 100; i++){
    if((i%2)==0){
      even.add(i);
    }
  }
  return even;
or

  (filter even? (range 0 100))


The GP qualified that this is better with newer JDK versions. For instance, in Java 8 you can do:

    IntStream.range(0, 100).filter(i -> i % 2 == 0);


I find i -> i % 2 == 0), still less readable than even?.

But let's step up the game a bit. Let's produce a sequence of the form [true false false true true true false false false false ...] of exact length 1000.

  (take 1000 (mapcat (fn [i] (repeat i (odd? i))) (range)))


Using the Java 8 'stream' API:

   IntStream.iterate(0,  i -> i + 1)
	.mapToObj( i -> Collections.nCopies(i, i % 2 == 0) )
	.flatMap(Collection::stream)
	.limit(1000)
Generate an infinite series of incrementing numbers, starting from 0, map each to a stream of boolean indicating true/false, flatten that stream of streams, then limit the output.


Nicely done :D! You're the first one to get it right.

Yeah, I'd still argue that the clojure stream api is more readable though ;).

  (->> (range)
       (map #(repeat % (odd? %)))
       (apply concat)
       (take 1000)
The thing is that these additions slowly creep into java, which is great. But you still have to wait for the mercy of the language designers.

Clojure is so flexible that it is possible to bring these things in as libraries, resulting in a very lightweight and stable core, and a very rapidly evolving ecosystem.

Also the persistent data-structures of Clojure make life so much easier. It's basically a language build around persistent maps and arrays.


> Also the persistent data-structures of Clojure make life so much easier. It's basically a language build around persistent maps and arrays.

When programming in Java (which I did for 14 years), I always thought about the little machines I was making and how they interacted. In Clojure, I'm thinking about what shape the data should be and what stack of stencils and folds (as a visual metaphor) I need to get it into that shape.

That is to say, Java is object-oriented, Clojure is data-oriented. So take data-orientation and add easy-mode concurrency and you have something wonderful.


To close standard libs gap, let's assume I have static functions for odd() and take() as well as Java8 stream APIs statically imported. After this, it looks pretty compact and readable:

    take(10, i -> range(0, i)
                    .mapToObj(j -> odd(i))
                    .collect(joining(" ")))
    .collect(joining(" "));
For reference, odd and take will look like this:

    private static String odd(int i) {
        return Boolean.toString(i % 2 != 0);
    }

    private static <T> Stream<T> take(int n, Function<Integer, T> f) {
        return Stream.iterate(1, i -> ++i).map(f).limit(n);
    }


To paraphrase Hickey, "I find German less readable than English; that doesn't mean it's unreadable."


How about this then?

    IntStream.range(0, 100).filter(::even);


Not quite what was asked. The intended outcome is a sequence of length 1000 where the elements are 1 * true, 2 * false, 3 * true, 4 * false and so on.

To compare it with what your code produces.

[true false false true true true false false false false ...]

[true false true false true false true false true false ...]


How much assembly have you done? "for"/"while"/etc are coming out of structured programming.


Yes, but that in turn was inspired by the patterns and idioms in assembly that didn't fuck up your codebase.

Simple conditional forward jumps, if, simple conditional backward jumps, while, and simple conditional backward jumps depending on a variable, for.


Imperative != low level.


Because of the way our current hardware is build, it is actually Imperative <=> low level (ignoring forth for a bit).

Because impressive constructs have a somewhat straightforward compilation path to assembly. Even when GC if involved.


I think he meant in the sense of low-level because of the language primitives offered versus what is possible with clojure (which lets you define your own essentially).

Edit: I see others have offered more thorough answer while I was reading sorry for the redundancy ;p


I find the readability is where Java's verbosity really hurts it. E.g. if you bring up the horribly verbose getters/setters in Java many people will respond that you can autogenerate them - which is true as far as it goes. But you can't "autoread" them, and if you have a thousand getters/setters of which ten provide different functionality to the standard field access, that makes the code very hard to read.


I don't find much validity to this argument that constantly comes up. Out of the box, you have two options and neither are bad... (1) Make your variable package/public/protected scope and refactor one day if you have to. (2) Use getters/setters, possibly with shorter names (if your fancy framework allows that) and more terse formatting.

Is this so bad?

  int size(){ return size; }
  void size(int newSize){ size = newSize; }
The setter shouldn't be so common anyhow because immutability is better.

Besides, if you have a thousand getter/setters then your problem might not be Java.


Most coding standards don't like that, and if you can't autoformat your code then you have bigger problems. I do agree that some of the problems are cultural rather than inherent to the language, but even if we do that,

    public class Potato {
      private final int size;
      public Potato(int size) { this.size = size; }
      int size() { return size; }
    }
makes poor reading in comparison to

    class Potato(val size: Int)
(I didn't mean a thousand getters/setters in a single class, but across the codebase as a whole)


> I find the readability is where Java's verbosity really hurts it.

I'd agree, but on the JVM there are plenty of statically-typed languages with clean interop that permit more concise expressions than Java, so I don't see conciseness alone as a reason to go to a dynamic JVM language. (That said, Java is getting better at this over time, though its still, to me, a pretty big negative for Java.)


For me, readability is directly tied to good abstractions (and meta/programming facilities for making good abstractions) and a certain degree of conciseness.

If I have to read 5-10 lines of intermediate code to figure out what's going on, it's less readable than if there's a short expression.

Some languages seem to be driven by fear that if you provide programmers powerful facilities for abstraction that rise to the level of augmenting the language that developers will no longer be able to figure out what's going on without reading a lot of code, so they solve this problem by forcing developers to write (and therefore read) a lot of code.

Java seems to be one of those languages.


Yes, Java is a mature language.

But any perusal of rosettacode.org is fairly indicative that other languages, while less mature, have adopted some good ideas.

For example, here's Quicksort in both Java and Elixir:

    public static <E extends Comparable<? super E>> List<E> quickSort(List<E> arr) {
    if (!arr.isEmpty()) {
        E pivot = arr.get(0); //This pivot can change to get faster results
     
     
        List<E> less = new LinkedList<E>();
        List<E> pivotList = new LinkedList<E>();
        List<E> more = new LinkedList<E>();
     
        // Partition
        for (E i: arr) {
            if (i.compareTo(pivot) < 0)
                less.add(i);
            else if (i.compareTo(pivot) > 0)
                more.add(i);
            else
                pivotList.add(i);
        }
     
        // Recursively sort sublists
        less = quickSort(less);
        more = quickSort(more);
     
        // Concatenate results
        less.addAll(pivotList);
        less.addAll(more);
        return less;
     }
    return arr;
     
    }
now Elixir:

    defmodule QuickSort do
      def qsort([]) do
        []
      end
      def qsort([pivot | rest]) do
        { left, right } = Enum.partition(rest, fn(x) -> x < pivot end)
        qsort(left) ++ [pivot] ++ qsort(right)
      end
    end
The advantage here is NOT just an "economy of typing."


The readability argument is overused and maybe misunderstood to me.

An experienced programmer is supposed to know the language well as well as the API and frameworks.

From my experience Java is quite verbose and the standard frameworks are among the most complex. It is typical for a clojure/scala/python program to require 1/2 or 1/3 the lines of codes than Java. And they require much less configuration files and annotations.

How much time a developper spend to read his operating system code? How much time a developper spend to read the API implementation code of his language or even the framework ? No time because the code is great, work as expected and doesn't require to be read all the time.

That's what a more expressive language provide. At greater set of standard language construct, API and frameworks that allow you to express the same in a much more concise way. This is much easier to read because the fundations are rock solid and the meaning of a function doesn't evolve over time, it is also nearly bug free. You reduce the accidental complexity.

When you choose a less expressive language like Java, even with the most advenced frameworks you have much more unreliable custom code. You are in fact increasing the readability issue, not reducing it.

Sure there the fact that many more developper master Java and the associated frameworks than say Scala or Clojure. But this also act as a filter.

You can do the same thing with a team of 5 experienced clojure developpers than with 20-30 java dev. A part come from the language expressivity itself. A part is that it is much more likely to get bad developpers in Java that will mess your code and make you loose time. It is easy to find Java dev, a bit less to find great Java developpers. Another part is that the bigger the team, the more is lost in communication, meetings and politics than actually doing things.

So that your choice ! Do you prefer 5 clojure dev, maybe paid 20-30% more or 30 java dev with 5 managers (paid 20-30% more) competiting to get budget and projects ?


This may be due to Android being limited by Java 6, but I find Java code to be so verbose that it hurts readability. Unnecessary try/catch/ignore just to close a resource, tons of factories, classes with static methods because you can't add the functionality to string, etc.

What weirds me out is that they didn't even consider Kotlin an option, since it is essentially a language so that you can code Java, but without the javaness.


You might call it 'unnecessary' but if that exception's going to be thrown somebody's going to catch it, and you don't want it to be the android runtime.


Well, not everyone thinks Java is easy to read. I've doing my job with JavaScript and CoffeeScript for more than two years and I still hates JavaScript, let alone Java. And CoffeeScript is way better than it, even it's simply just some syntax sugars. I'm learning Clojure and it's even more expressive than CoffeeScript and that's why I like reading Clojure.


Ouch, got downvoted in under a minute. Guess it's not cool to say anything positive about java! :) I still would have liked it if the authors of the article had said something about why they had a "strong aversion" for java.


I'm not a down voter, but there have been volumes written about the pros and cons of Java. Every possible corner of every possible argument has been explored ad nauseum. I'm grateful they didn't waste my time rehashing the endless arguments. As far as I'm concerned, Blub beats both of these languages.


Forced object orientation?


Clojure wins on readability as well as writability. The easiest way it does that is by drastically reducing the amount of code you have to write for whatever random thing you'd otherwise do in Java. It opens the door even further for additional savings compared to other languages in a similar place like Python or Ruby by providing macros.

Freedom from the Kingdom of Nouns alone gives you a big win in readability. Naming things is hard, and when the thing you care about isn't the Class but the Method then bothering to name a wrapper Class is additional overhead, an extra "what is this in my code?" that you need to make room for in your brain -- additionally it's common practice in Java world to put that Class in its own file all by itself, often in a totally different namespace, and often physically far away from where it's actually used when looking at the files on the filesystem, which further increases the cognitive load of knowing where your code lives. IDEs in the land of Java are helpful only because they are necessary because without them the cognitive load is just too much for any reasonably complicated project.

Freedom from mandatory static typing of the sort Java has saves you from pointless class wrappers, pointless POJOs that must have names and live in their own files and namespaces, you are free to just use maps of keywords to values. Free to work with the data directly rather than an ad-hoc aliasing scheme that might decide to hide things you need or surprise you with nulls. When you want protocol or interface adherence, it is there for you, but it is not mandatory for everything.

Maintaining Clojure is easier because there's less of it. A new hire can be pointed to your code of n files and told to spend a week reading and playing in the REPL, versus pointed to n2 files and told to spend a month reading and inserting breakpoints here and there to understand all the chaotic flows across what and where. You have a legitimate shot at picking 10 random Clojure files out of the project and understanding a bit about the data flows in them in relationship to the project as a whole, but in Java you're going to need a lot more context, especially if you get unlucky and pick out 10 MyCustomException classes. What is there in Clojure can be very dense, but it can be apparent in code review when you went too far, and then it's easy to refactor and you don't need any auto-refactor tools. You just substitute expressions, add comments, and name only what needs to be named, you can do it all without constantly needing to restart your program because (unless you're using JRebel) you added a function here or changed a parameter there.

Java is good for risk-averse corporations -- a single average Dev on an average size team can't do very much, many corporate programmers average around 1000-2000 lines of code per year* even with their IDEs helping them, and because of that they can cheaply be replaced or moved to another part of the project.


It's great to see such an "exotic" programming language like Clojure getting some serious traction. And by exotic I mean a language that stands out from the imperative or object-oriented crowd.

I like how Clojure challenges established paradigms with a surprisingly mature ecosystem.

Being a hosted language on the JVM has its pros and cons, but the cleverness and pragmatism of Rich Hickey (creator of Clojure) really stands out. He decided to go with an industry strong, battle-tested environment, effectively standing on the shoulders of giants, while providing a lot of fresh ideas.

Also, the fact of being a Lisp can be seen from different angles: for some it's lots of weirdly nested parentheses, for others it has a beautifully simple, yet extremely powerful and expressive syntax.

But don't let those aspects fool you. Once you scratch a little bit the surface, you will see the extraordinary power of its persistent data structures. For those who don't know what persistent data structures are: in Clojure, oversimplifying, there is no mutation to data structures. Every time you "add" or "remove" a key from a hash map, you are actually creating a copy of it. And in order to make this efficient, you need special data structures with "superpowers", that are not actually creating a whole copy of themselves, but rather modify atomically only a part (hint: trees).

And having those data structures leads to the next big feature of Clojure: concurrency. Not only does Clojure provide a mechanism to dispatch thread-like constructs (core.async), but the whole language is designed around supporting concurrency.

I also want to add that what really surprised me about Clojure is its thriving community and smart people using it. I am pretty sure that if you want to hire great, highly motivated talent, going Clojure will help you with that.


Parentheses is no a problem if people really want to fix it. We already have JavaScript fixed with CoffeeScript, there's also a chance to fix Clojure with indentation-based syntax, like I tried

https://github.com/mvc-works/parser-combinators/blob/master/...


> Once you scratch a little bit the surface, you will see the extraordinary power of its persistent data structures.

I've heard this repeatedly, yet in the examples I've read (for web servers and for web ui work with cljs), they're using atoms with `swap!` to provide "current state".

Could you provide an example of using persistent data structures in a web app? Or am I mis-understanding?


You're compare-and-swapping out persistent values with an atom (it holds a succession of immutable values over time). It's not an example of people shirking persistent data structures for mutation.


Yes, I understand that. I guess my original question would have been better put: can the notion of "current state" be avoided with persistent data structures, and if so, how?


I'm a hardcore imperativist right now, so don't take this as authoritative, but here's my understanding of how that works:

Think of your data as one reality in the set of all possible realities. None of them exist or don't exist, they'll just receive human attention or they won't. Instead of an inbox with an ID and a current state, you just store events that happened and a user might say show me the end state of the timeline that started at event d79se5k. They might also ask you for the timeline starting at a different point.

There's no "current state". You're just ready to answer questions about hypothetical realities. And while some realities are more likely to be computed (and cached) that's only because they correspond to some key facts that a human is going to look up.

Put another way, imagine a soup of unrelated facts. Your code is a way to project those facts into different mathematic spaces. You never just say "what's the current state" you have to actually write out specifically what you mean as a query.

Sorry for the vagueness. I'm just putting out there my fuzzy understanding until someone else can write something better. :)

I'm really interested this approach, and I think a lot of data should be immutable. Sadly, a lot of people are treating immutability as a religion these days, which causes problems when computation is better suited to imperative structures.


I read this article yesterday because I am having a language choice issue, between Ruby and Clojure.

I am in my (almost) mid 60s, and except for being on retainer for advice I don't really accept consulting work anymore. I have a long list of projects that I want to do, mostly open source, and settling on one language for a few years would make things easier.

Clojure and Ruby both support DSL development very well which is important to me and both play very well with Java (via JRuby). I am starting to ask myself though if I wouldn't just be better off with Java 8, even without good support for writing DSLs. I have mostly stopped using Haskell and Scala.

When I was much younger I totally enjoyed learning and using many programming languages but as I get older I feel more like just getting stuff done, especially since I dedicate much less time now to technical pursuits. So in a sense I am facing roughly the same type of decision as the Metabase people.


I follow your blog. I'm a month from 40 and agree. After several years working mostly in Clojure, Scala, and F#, I am finding myself most productive in traditional languages even though they're less interesting. And am finding projects more interesting than implementations, which is probably the way it should be.

I'm finding now that OOP is actually a pretty good model for most things. Loops and mutation reflect reality: you do a task N times and write down each result; you don't take the first N results from an infinite seq of tasks. IoC is useful. In addition having a much larger set of things to copy/paste from github/stackoverflow is nice.

That said, most (all?) of my paid work is pretty banal, nothing that's really begging for functional style. And for whatever reason I've never had much success with REPLs. Getting them updated correctly with the code and dependency changes, typing in what I want to test, then running it always seems to take me longer than just executing the app and testing changes directly.


I'd personally look to find what language fills the gap that the current set of languages I know don't fill. Clojure is my filler for lisp language and language with good macros support.

I use Ruby in the slot that many have Python in but with added coolness of metaprogramming not found in most scripting languages.

And to add to that a bit, I found that recently it's been getting harder finding new languages with enough new patterns and paradigms to keep me interested in learning and mastering them.


Out of curiosity, why not Haskell or Scala if you already know them?


> .. However, we had a strong aversion to using Java itself ..

Honestly, why? Seems odd given Java's great track record for exactly this problem and huge library and developer share. The code itself is forced to be static and explicit which makes for easy onboarding for new developers.

It's a bit strange to just dismiss Java in the context of the rest of the article. You're kind of staking your company on decisions like these, and making them because of some unquantifiable 'strong aversion' seems like a poor choice.

That said, Clojure or other lisp base definitely seems like the correct choice for a query language / dsl. That's the shining point of lisps after all.


I think the popularity of yesql (https://github.com/krisajenkins/yesql) calls your last point into question. Indeed in my experience, using korma (the most popular clojure SQL-DSL) provided no additional benefit, just additional documentation to read.

I've gone through the FP language snob mill over the last few years, and have to agree -- I don't love it, it doesn't make me feel all chipstered out, but Java is actually a pretty decent language for getting stuff done.


I built a system on top of yesql* and eventually threw most of it away because it was a lot more trouble than composing queries with honeysql. I prefer honeysql to yesql for the same reason I prefer hiccup to enlive. It's just easier to compose things with native data structures using the full clojure language.

* I even maintain a fork of yesql powering several of my production apps to use that has features that were never merged (such as making it possible to insert maps as jsonb into postgresql by deferring to the underlying jdbc libraries for serialization).


I'll have to give honeysql another look. Superficially it looked like Korma, which turned me off for the exact reason mentioned in yesql's "rationale" section, so I didn't investigate honeysql any further.


You can use it like korma if you want (it has a higher level dsl (but with functions that all return a map)) ala:

    (require '[honeysql.helpers :as sql])
    
    (def query-map (-> (sql/select :*)
                       (sql/from :foo)
                       (sql/where [:= :bar "baz"])
                       (sql/limit 5)))
This works because each of these functions has two arities, the lower one returning a map, and the higher one assoc'ing or merging (there are merge-* functions for building purposes) something into its first argument (which is presumed to be a map).

    (= (sql/from :foo) {:from '(foo)})
This means that if you just need a SQL AST, you have this handy map thing that honeysql can turn into platform-specific sql. But if you just need something quick and dirty, you can use something that looks very similar to korma. There is no ORM functionality, but with a query building toolkit, I haven't really needed it. Since honeysql's helpers are built as functions, I haven't even really needed to dive into the lower map representation much, although it was handy the one time I was translating a higher level query language into sql.


personally, i find java pretty painful to use; it's definitely capable of doing whatever job you have in mind, but when i use it i have the constant feeling i'm using a suboptimal tool. and then it leads to reams of boilerplate all of which has to be maintained, so it's not just a development-time cost; there's the depressing feeling of having to deal with it every time you want to read the code. clojure would be worth it just for the joy of using a tool that fits well into your hand.


It's kind of like building a model with clay or with sticks. The clay model is going to look nicer with rounded curves and no wasted points. The stick model will have bits sticking out.

However when you get 30 people working together to make a big model, the clay model is going to start looking very strange indeed. Different people will have different finger pressure and shape the clay differently. Some people will make more right angled parts with the clay which will clash with the parts of the clay that are carefully smoothed out. Someone might spend a week fixing a perfect parabola for a curve at the top of the model just for someone else to bend it off to the side with an ugly twist to fit with the other side of the model.

30 people on the stick model? Not really a problem. All the sticks look the same. Anybody who needs to attach one side of the stick model to something else just needs to grab a stick and tie it on. You won't see a perfect stick parabola, but then you also won't be stuck with a strangely twisted piece of clay.

You can always create a style guide for using the clay - always keep between a 30 and 60 degree angle on joins. Apply one finger of pressure when doing joins. And so on. Sometimes this works with very diligent reviews that keep everything in place, but as soon as a crisis comes up or the model needs to pivot urgently? Everyone rushes to touch up the model in the quickest way possible and your model starts to get a bit thinner on one side than the other, etc.

So there's nothing wrong with choosing a great clay model that fits well into your hand and you can shape effortlessly - but you need to remember that sometimes the 'painfulness' of using a model made out of sticks can be a big blessing when you're trying to massage a bit of clay that someone else pressed a bit too firmly on.

EDIT:

Also, when you've been making and using models for awhile, the stick models get an added benefit: you can browse through the stick models really fast based on only their outline and be able to understand very quickly how they work. Pretty clear why: the rigid sticks form a very distinct outline (think interfaces and forced directory structure in Java). Being a stick model expert therefore has very good job mobility and high demand.

Each clay model is generally more unique and requires some study to understand even if it does look nicer.


I will try to continue your clay/sticks parable, but in my limited clojure experience, you seldom create a clay model, it resembles more a creation of a custom clay brick-set.

Most of the time, all of the team is either building bricks or using those bricks.

The biggest plus of using clay instead of sticks is that when you find yourself in a corner, it is easy to invent a new brick shape that fits.

The drawback is, that without the style-guide, you can easily end up with brick-shape per developer. But I see a difference between "Hey, why did you invent a new brick type again?" and "Apply one finger of pressure when doing joins."

When crisis comes and model needs to pivot urgently, there are two possibilities. Either you just need to rearrange your bricks. Or you realize there is something fundamentally wrong with your bricks, and then you create a new brick-type and carefully replace old bricks with new bricks.

Ok, programming parables are weird. I might write a more concrete example, if I have time.


Nice extension! Feels right too, clojure does feel a lot like passing around similar clay bricks. When all the bricks line up, it's kind of like Lego?

The analogy also opens up another difference. In clojure, you often end up passing around a lot of maps with string keys. What are those keys exactly? You have to crack open the clay brick to take a look at how it works. With the stick model, you can often see how the sticks work if the bundle of sticks has been color coded nicely - eg, well constructed generics. Luckily cracking open a Clojure brick is as simple as ctrl-clicking, so it's not nearly as bad as it sounds!

Programming parables are definitely weird though - programming does not have true analogies with the real world. It's more of a superset of the real world and the use of programming languages is more akin to defining physical laws than building bridges with clay or sticks. I don't think we as humans have really come to understand exactly what we are unlocking with the development of programming and how vast it really is.


My experience is Java costs me around 10x the number of lines of code (and I mean code, not imports) to get the same things done. The brief syntax and higher-order programming eliminate so much from my screen that, in hindsight, served as little more than visual framing for sparse bits of code.


If there's no single language lock-in, then using polyglot approach, you can get best of both worlds: write DSL parts in Clojure and other parts in Java. We did this recently in one of our porjects (Clojure piece was responsible for interacting with ElasticSearch).


"So, in summary, we chose Clojure for JVM threads + Database drivers, the ability to ship an uberjar and the ease of expressing tree manipulations which were the core piece of complexity in our backend."

for people who may not know clojure here is an example of tree manipulations, it's really powerful especially in UI programming which is all about manipulating recursive structures: https://gist.github.com/dustingetz/c11ae4edb9e0d14787d2

Scala has answers to this too (I would love for someone to reply with the best scala way) but for whatever subjective reasons the clojure way makes me happier for ui work.

As to scala pushing towards typed object mappers, here is one example of generating untyped queries with Anorm [1], I'd be surprised if this couldn't be made to work for their query language, but that said the scala ecosystem has a lot of, shall we say "diversity of opinion" about the Proper Way To Do Things, so I am not surprised that newcomers to the language had a bad time.

[1] (2013) http://www.dustingetz.com/2013/03/26/orm.html


This is probably going to be a very unpopular opinion here, but I think Clojure is a horrible language to work with.

I don't have the experience with non imperitive languages which makes learning clojure a really steep curve. Also the error you can recieve with clojure are many times a big mystery, I've never seen as bad error reporting as I've seen with Clojure. Other issues I have with it is that Clojure-fans tends to want to write EVERYTHING in clojure or lisp style code. That includes stuff like Javascript, HTML and similar stuff. Things that just makes it even more complicated.

I have really tried to learn it several times but got so frustrated I quit. We have a small thing at work which is written in Clojure and no one knows how to fix issues with it since no one has any experience with Clojure.

Clojure does not fit my brain.


I think your criticisms are definitely valid.

The learning curve is steep (intentionally so) but definitely worth it. Your programs will become more reliable and you'll get more done as a consequence. Please push through!

The error messages are terrible, and there are a lot of ongoing conversations about them at the moment in the Clojure community[1]. The Clojure[script] toolchain does tend to be all-in but there's work happening now to break that up — stay tuned. StackOverflow and the Clojure Slack channel also full of people who know how to fix issues :)

Stick with it, when you'll have an "ah-ha!" moment soon enough!

[1]: This talk is a good backstory as to why the errors are so bad, and useful no matter what language you use https://www.youtube.com/watch?v=FihU5JxmnBg


I work only with Clojure and ClojureScript (including a simple DSL which lets me write HTML in Clojure-syntax).

Error reporting in Clojure is bad - no two ways about it. Once I got used to scanning the JVM stack trace, it wasn't so bad. But it could be better.

I really prefer writing ClojureScript to JS: using one language, with the same data structures, is a seamless experience. I like writing HTML in Clojure data structures, as it makes manipulating the data much easier.

But your mileage may vary here! Clojure won't be my last language. :-)


Definitely agree with you on the bad error reporting. As to the other part, at some point you have to make a mental adjustment to the "Lisp way" of doing things, and that may not be easy. Out of curiosity, which materials (online, books, etc.) have you used when trying to learn Clojure?


Clojure = Functional programming + Lisp + Decomplecting.

The first two are big enough to spend months on. Expecting to learn it all in a week(end) is unrealistic. See "Living Clojure" http://shop.oreilly.com/product/0636920034292.do

Your brain will thank you.


Kind of shocked that building database libraries for python (homebrew install gcc? pip install foo) was "hard" for them.

I find the Python ORM options (sqlalchemy/Django/etc) lot better evolved than what is available for clojure and couldn't image that being a roadblock. Korma is basically a query builder and lacks a lot of things I was used to, and I had to engineer a declarative layer for defining models in the ways I wanted.

Pain in setting up developer machines could be simplified with an ansible playbook pretty easily.

OTOH, one of the worst things I hate is trying to figure out how to automatically get around Sun's paywalls and fiddle with Java classpaths :)

I do feel some level of want to avoid vagrant in the development workflow, in which cases I'm usually fond of just logging into a real VM. I do think it's potentially good for tests.

IDK, I always want to solve the idea of "fragile deploys" with immutable systems in prod. Uberjar was cool, I'll give it that -- but the startup time of clojure was often not worth the wait.

My biggest complaints with clojure is documentation quality, inconsistency in return types (or just willingness to return nil in error scenarios), and tracebacks that are very difficult to sort through.

Clojure also presents some problems with the idiomatic nature of trying to roll up everything into one statement, whereas adding debug and debugging require unrolling it, than packing statements up again to be socially acceptable idiomatic clojure.

Testing in clojure also strikes me as painful, as it tends to punish you with "let" causing an indentation every time you need to assign a variable, something that is useful in tests that want to check results a lot along the way.

My gut feeling is clojure was good for a little light scripting on top of a big Java codebase (kind of like you might use Lua in a game engine) but would not be something I'd pick for a primary language. Primarily using Java libs would also solve some of the ecosystem problems.


It's not hat "homebrew install gcc? pip install foo" is hard for us, it's that we didn't want to support 1000s of people trying to set it up on their machines.

Every possible source of error we eliminated meant we'd need to spend less time on twitter debugging installation problems. It's useful to remember that our target install base isn't python developers who might want to hack away on a python project, but rather analysts, end users, DBAs and all kinds of people who've never heard of homebrew and who might not even be comfortable with using a terminal in Mac OS X let alone compiling things.


So why can't you release an installer? You are not compiling .NET. If many desktop applications are either written in Python, Java, or C++, so what prevents you from releasing an installer? Heck, even Go programmers have to think about compile cross-platform.


> Heck, even Go programmers have to think about compile cross-platform.

Not that much in Go 1.5 :) On the last project I worked, just setting GOARCH and GOOS worked out of the box.


For what it's worth, Korma isn't an ORM such as what you would use in Python. It's a relatively thin wrapper around SQL queries. I don't know whether they found that to be plus or not, but it would put it in a slightly different category than the Python ORM options.

I'm also betting that the startup time of the JVM wasn't terribly important in their scenario.


Yes, I'm aware it's not an ORM. It's pretty primative by comparison and I wanted a good Clojure ORM at the time.


The fact that it wasn't an ORM was a huge win. We effectively had to build a dynamic ORM that modeled our end user's data warehouses which is a very different problem than a normal web app's database access patterns.


sqlalchemy is my favorite library for pretty much any language. It's fantastic and a huge part of the python draw for me. The only downside is it doesn't play well with new async python tools, but python is totally fine at not being async


> Python’s Mysql and Postgres database drivers required compilation. This made both developer machine setup as well as deployment more complicated than it really needed to be.

Am I missing something - this was really a such a problem that it was a factor in switching languages?


I've started interpreting a lot of these "Why we use X" posts as less 'bragging' and more these companies trying to justify their own choices. In part because it perhaps makes them feel less isolated in their choices, and in part also because by hoping that others follow them they'll find it easier to recruit, something that is easily underestimated especially outside of major hubs such as NY, SF, or London.

Perhaps I am being uncharitable. I note with interest they say, "However, we had a strong aversion to using Java itself", but don't explain why they had that aversion, or whether it was based on anything but the fact that Java isn't trendy.


Off the cuff, I'd estimate that between the team members at the time, we'd shipped perhaps 100k+ LOC of java into production systems at past jobs.

It didn't seem interesting to rehash why we didn't want Java's verbosity in a small team.


Yes. It's also a problem when you're deploying to, for example, EC2 instances that are too limited -- say a t2.tiny for proof-of-concept work -- to compile the Python lxml library.

That's after you figure out which 50 different *-dev libs you have to sudo apt-get install, in a tedious process of trial-and-error, ./configure, make, and error.

With Clojure this is a non-issue. lein will download what dependencies you need. And they're all already compiled into .jars. This is a benefit of the JVM infrastructure, but we're comparing Python here, so.

And of course you can upgrade the EC2 instance, but an annoying and time-consuming process, since you're not in control of the AWS account yourself, and the entire issue doesn't exist if you're using Clojure.


> to compile the Python lxml library.

If you have to do it very often, I've had good success with keeping compiled python wheels around.


It sounds innocuous, but we were planning on supporting a lot more databases, and when you start needing to build drivers for MySQL, Postgres, SQL Server, Oracle, Redshift, etc etc anything that makes this step more brittle starts to add up.

There are a lot of things that are mostly painless if you're deploying and supporting a single instance that become nightmares at the scale hundreds or thousands of installations you don't control.


It matches my own experience too. Libraries that need to compile c are a deployment nightmare.

Installing Oracle client is even worse though ;)


Isn't that what Python Wheels are for though?

http://pythonwheels.com/


(pythonwheels doesn't do mysql nor postgres nor sqlalchmey nor ... most DBs in fact?)


It might be if you were planning to deploy to end user's machines.


precisely.

(author of the post)


One of the things that I love about Clojure is Leiningen. It just takes away the headaches of setting up projects in Clojure. I think the only other tool I'm aware of for another language that is quite as useful is probably NuGet. I've never had issues using lein, just drop it into my ~/bin/ folder (I'm using openSUSE so that folder is in the system path by default), open up a terminal and type in lein and it works. I don't even need clojure installed, just the JVM, it will download clojure for you as needed!

Props to those working on lein.


And yet maven does the same thing but gets no love from anyone (including me!)


Not sure if Maven can be compared to lein, never personally used it, not knowingly at least. I've heard great things about Gradle though.


If Gradle's so great, then why does it use a JVM language that wasn't considered by Metabase.

> The shortlist really came down to “something on the JVM” [...] However, we had a strong aversion to using Java itself, so the real short list was Scala or Clojure

Gradle requires the configuration files to be written in Groovy, and is itself written in Java and lots of Groovy.


I do know Android Studio likes to rock Gradle. Clojure has Lein though, why would you use Maven then? :)


Can someone expand on what they mean by lightweight threads for Clojure? My understanding is that lightweight threads (green threads) is what java/jvm explicitly moved away from way back when; it's user-mode threads bound to one processor, so if the processor gets busy, then all of the lightweight threads for that processor get blocked. Meanwhile, Java/jvm uses kernel threads and context-switching so you can do real multi-threading.

I'm aware of how Quasar purports to be the best of both worlds, and it looks like Pulsar is a Clojure API for Quasar, maybe that's what they're using.

It doesn't appear Scala has something comparative, but I have a hazy sense that Scala people believe that Akka and monads are better for easy asynchronous programming.


Clojure's core.async has "go routines", (this is more or less the same as in go), basically you can put and take messages on a channel. These operations will block the green thread, so you end up writing synchronous code. The idea is that synchronous, blocking code is easier to understand then async code. Since these are lightweight threads it will scale better than using real threads. I'm pretty sure it's fine to have hundreds of thousands of go routines.

This abstraction sits on top of java's real threads, so you can have several real threads doing the work and they can switch between the go routines once the go routine hits one of the blocking operations.

Or something like that... read this: http://clojure.com/blog/2013/06/28/clojure-core-async-channe...


Scala has Future (which yes does form a monad) which is very much the same thing as Clojure's core.async macro or Quasar's annotation-and-bytecode-manipulation and is very much directly comparable, it's just Scala's type system is powerful enough that you can do that in ordinary code rather than a language-level special feature. Akka is also an option, though not one that I like.


Core.async uses code rewriting to turn blocks of code into state machines. From there puts and takes from channels become state transitions. It works almost exactly like async/await in C# or Yield based generators in other languages. The cool bit is that core.async does this as a library, not a compiler level feature.


It's likely that they're refering to core.async which is a Clojure library that provides IoC threads via the `go` construct.


Now, choosing a programming language for a project is usually decided on a mix of what a team is most comfortable with, beer-fueled debates on dynamic vs static typing, benchmarks of marginal relevance and that elusive quest for Dev-Hipster points.

How about this for an interview strategy: Have these discussions. Hire the people that realize most of the above mentioned things are meaningless.

(Clarification: Only the "comfortable" part isn't meaningless. But there too, it's only as meaningful as the placebo effect is significant.)


How is "dynamic vs static typing" meaningless?


For most applications, either can work. They both have to be managed correctly.


What are "most applications"?

How do they have to be managed?



I don't think an interview strategy of the form "has the same values and opinions as stcredzero" is going to work for everyone.


Of course not. Some people really like their illusions.

But seriously: Having been part of a community that thought their language made them "super-programmers" I can say this: It's possible that on some level, the language does give a significant tooling advantage. However, this is often going to be completely swamped by various soft/human factors.

A baroque quartet consisting of very mature musicians who work supremely well together is still going to produce far better music than a "super-group" of ego-driven martinets who secretly despise each other. It wouldn't matter how technologically advanced their instruments were over the 1st group, even if those instruments played themselves.

It's the group that truly gels, has healthy interpersonal dynamics, has relevant experience, and healthy practice habits that is going to be the better musical group.

Resolving the analogy left as an exercise.


Considering their criteria Erlang/Elixir seems like a huge omission from the contender list.


> Wide variety of mature database drivers

That requirement was probably not met when they started.


Does Erlang lack a variety of mature database drivers?


I'm not an Erlang dev, but a quick look at the Erlang docs shows:

"If you need to access a relational database such as sqlserver, mysql, postgres, oracle, cybase etc. from your erlang application using the Erlang ODBC interface is a good way to go about it."[0]

"It is built on top of Microsofts ODBC interface and therefore requires that you have an ODBC driver to the database that you want to connect to."[0]

"But currently it is only regularly tested for sqlserver and postgres."[1]

"But currently it is only tested for Solaris, Windows 2000, Windows XP and NT"[2]

[0] - http://www.erlang.org/doc/apps/odbc/databases.html

[1] - http://www.erlang.org/doc/man/odbc.html

[2] - http://www.erlang.org/doc/apps/odbc/getting_started.html


Compared to something like JDBC et al, Erlang DB drivers are pretty half-baked


Lisp has been around a while, Java has been around a while. Clojure is the perfect combination and I predict it will be around a while.


Lisp has been around a while. Java has been around a while. Clojure is the perfect combination and I predict it will be around a while. It's exhilarating to program in Clojure. That's the only word I can find to describe what I feel to program in that language.


I had this conversation on twitter already: typed object mappers seem like a questionable choice for the problem space. Since it seems like all of their work in Scala used them, I'm not surprised they hated it.

(Disclaimer: I have no particular liking for Scala.)


I'd be interested to understand how Korma solves their problem. It's macro-based so I wouldn't think it would be particularly good at dynamically restructuring queries based on runtime variables.


Yeah, clojureql was much better for this sort of thing but is to all intents and purposes abandoned.


Why wasn't Erlang/Elixir even considered? It checks off all their initial set of requirements


You know what else is easy to work with and deploy? PHP.


I wouldn't call a language that encourages writing spaghetti code and creating dependency hell to be "easy to work with", much less one which doesn't even pass its own test suite, which implies that even if you do everything 100% correctly, the spec itself is faulty, so you WILL have non-deterministic "3AM phone call, up till 3PM fixing" type bugs... No thanks


Has anyone here ever heard of Metabase before? Their github page indicates there are 3 (three) contributors. Wouldn't it be safe to assume this post is primarily a marketing venture?


I've used Metabase before, it's a super easy way for a non-technical to do sql queries.




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

Search: