Why use Clojure?
paradiso.cc
paradiso.cc
What good is a single line of clojure vs 25 of golang if it takes 45 minutes to get that line of clojure working. I might have more bugs in my golang but the are trivial.
I've busted my ass on clojure for two years with various projects (systemverilog parser/manipulator, CPAP data decoder interpreter visualizer). I want to use clojure but I find myself dealing with awful error messages, constantly breaking tooling (cider), and dealing with asinine constructions all in the name of functional and programming directly in an AST.
I tried. I've read the awe inspiring posts about lisp from Graham and Fogus and Raymond and of course R Hickey and Granger and Hagelberg and Stokke . I think all these people are amazing!
After two years I have not gotten there. I can write reasonably sophisticated applications, but the productivity is so low I feel like in programming in Russian.
I got fed up with tree traversal the other day and said screw it. I turned around and rewrote versions of my CPAP app in golang, and then CPP in d a day. It took me weeks to get this done in clojure.
I couldn't get a binary parser framework to work except for the most trivial case, so I wrote my own generic binary parser. I couldn't tell if seesaw made swing better or worse. Actually I decides worse because at aleast with Java interop I can tell what the hell is going on.
In CPP writing a binary parser was nearly as simple as defining a struct and memcpy. God I live memcpy. You know how many years it's been since I wrote CPP? Never. I've never written a line of CPP before and the rewrite was almost pain free. The Makefile and understanding data alignment in struct a was the hard part.
I've found that when using Clojure you have to think in a different way about the problems to be able to "see" how your problem can be resolved in Clojure in a more elegant and concise way. This is hardest task I've found when moving to Clojure from Java: change your mind. With the right mindset, everything (in my case at least) seems like pretty trivial in Clojure, and definitely much faster to code and test than Java.
It shows the REPL output of each line of code as you write it, which is spectacularly handy.
Not to mention, LightTable started out as something special and morphed into something mundane. I have high hopes that it was just organiziation and the cool shit we saw at the begining will come back via plugins, but right now it's just not there compared to vim or emacs.
Also, it's not "foobar", but FUBAR: Fucked Up Beyond All Recognition/Reason/Repair.
take a collection -> split by is-even? -> for each collection, sort ascending -> take the first of each collection
This is the level of abstraction I want to think about. But, when one of these functions takes or returns something you don't expect, debugging the problem usually took me 30 minutes. The error messages were very opaque to me.I could never figure out a good work flow to debug these problems. I still feel like there's something I'm doing wrong. People on IRC tell me you eventually understand the errors better, they're just confusing at first. I never got to that point though.
> But, when one of these functions takes or returns something you don't expect, debugging the problem usually took me 30 minutes. The error messages were very opaque to me.
Yes, often true. E.g. in this case:
(->> [2 3 4 7 5 3 11 12 7] ;collection
(group-by even?) ;get a map of evens and odds
vals ;get just the values
(map sort) ;sort them
(map first)) ;first is smallest
(disclaimer: there's probably prettier ways to write this - I was just trying to keep it simple)I agree that there are sometimes inscrutable errors. For me it's often because of a type conflict between a function and what it's operating on.
Example: I'm a little rusty and when I tried to quickly code the example above I did this:
(->> [2 7 5 3] (partition-by even?) vals)
And got: ClassCastException clojure.lang.Cons cannot be cast to java.util.Map$Entry clojure.lang.APersistentMap$KeySeq.first (APersistentMap.java:152)
Doh! I wanted "group-by". And partition-by doesn't make a map! But why this specific error? Frankly I'm not sure. I'd probably have to look at the source. My sin of course was trying to compose operations in one go, rather than build them up by baby steps in the REPL. I don't think the errors ever get that much more readable, but you start to recognize the category of error you've made by the type of error that's thrown.Spending time on 4Clojure is very helpful to build your intuition for this.
(->> [2 3 4 7 5 3 11 12 7]
(group-by even?)
vals
(map #(apply min %)))</pre>
However, operation like this can be made much more readable if you actually use variables like this: (defn min-even-odd [xs]
(let [evens (filter even? xs)
odds (filter odd? xs)]
(list (apply min evens) (apply min odds))))
(min-even-odd [2 3 4 7 5 3 11 12 7])
Which is a bit longer, but I think it makes more sense.60,000% growth in 7 months using Clojure and AWS
http://www.colinsteele.org/post/27929539434/60-000-growth-in...
Against the Grain: How We Built the Next Generation Online Travel Agency using Amazon, Clojure, and a Comically Small Team
http://www.colinsteele.org/post/23103789647/against-the-grai...
At this point RoomKey is a lot bigger than Hipmunk.
If you wrote too high-level without enough details, people would call you out for "supporting your argument without any merit".
If you wrote too technical/low-level, it looks like a script kiddie toy and not "for production ready system with complex requirements and multiple stakeholders with evolving needs".
I gave up convincing people a while ago and just focus on get things done. Java, as verbose as it is, still productive enough for me thanks to the ecosystem + tools + libraries + etc.
Ask people who are working on "commercial" development how often they have to implement a Fibonacci sequence or something similar. The example comes across more like a neat party trick then something that would make itself useful on a daily basis.
fib-seq is not a defined token. wtf is going on here?
(rest fibonacci) fibonacci)
i don't know actually...This post is quite poor imho, and I am a clojure developer (I do use it at work as main language).
Maybe it's also because I don't find Midje appealing at all, or find a bit odd the minimalism argument when talking about lighttable vs emacs. If you know what you're doing with cider-mode (previously nrepl) on emacs there's no reason at all to switch to lighttable, quite the opposite. LT is probably good for newcomers, but for power users it's just not there yet (if ever).
Same goes for Marginalia, interesting and amusing, but I wouldn't use this for real documentation.
(def fib-seq
(lazy-cat [0 1] (map + (rest fib-seq) fib-seq)))
But, since he added an argument to the function, I don't think his code actually works, even if you ran :s/fib-seq/fibonacci[1]: https://en.wikibooks.org/wiki/Clojure_Programming/Examples/L...
Though I guess there is some merit in having less boilerplate, as fewer lines written mean fewer lines to modify during a refactor/debug session.
My understanding is that built in java hot swapping is very limited, and indeed would be a bad idea to try hotswapping in production.
If the clojure repl brings us closer to the erlang world isn't that a good thing?
Erlang does have a much better deployment story than jvms as far as code swapping.
But AFAIK Clojure is compiling to bytecode and is under the same constraints as the JVM that it is hosted in.
This is true for certain features (protocols, records, and gen-class, which are easy to avoid for everything but high-performance bottlenecks and legacy interop), but vanilla Clojure functions are built around vars, which are specifically designed to support reloading.
edit: it's still primitive compared to Erlang, but it's miles beyond Java.
Some links: http://blog.fogus.me/2010/06/09/clojure-rb/ http://stackoverflow.com/questions/4509782/simple-explanatio... http://jkkramer.com/sudoku.html
I really like Clojure, and it's what I started with in terms of Lisps. When I wanted to explore more "traditional" Lisp, I looked into Common Lisp, and it didn't thrill me. It felt old to the point of decrepit.
Racket feels very modern, and very polished. I'm trying to groom it as my go-to scripting language.
EDIT: actually this was the link I meant to post. It was on HN a couple weeks ago: https://docs.google.com/presentation/d/15-7qFy6URdE7Owi2Litk...
EDIT: Not this link, though it looks interesting: http://www.innoq.com/blog/st/presentations/2010/2010-05-20-C...
Except if you mean that you dabbled in Haskell, but are a Java/Scala/Ruby/Python/Pascal/C# guy.
In any case, it takes no more than 1-2 days (from scratch) to get to understand functional code. Remember that you weren't born able to understand imperative code either. Just learn the (very basic) syntax rules, and the rest is easy.
I recall interviewing a potential hire who lauded his appreciation and understanding of functional programming - and he really believed it. So I asked him to explain to me what a closure was, and give me some examples of ways you can exploit them in your code, practically. Pretty straightforward question for someone who claims to understand the concepts of functional programming.
Of course, I wouldn't be bringing this up if he was even remotely successful, but I don't think this was a result of his presenting himself in a dishonest manner. I think closures are subtle ideas; much like function application, composition, and other ideas that seem familiar and easy to understand until you are asked to apply them practically. That's when the gap between what you think you know and what you actually know is borne for the world to see.
From the little bits of dabbling with various lisps in have done a functional language is different enough from my bill paying regular languages (JS, php, Java) that without a focused study on a project that hits several layers of a typical stack, I fully expect the language to be foreign to me. Until I know the 350 core language operations by heart I'll not be able to play with them. If I cannot play I'll never be able explore the language and use and misuse it until I can make it do what I want it to do.
So the fact that it looks encrypted to you is a good thing. It's a code waiting for you to break it. It's a problem waiting. A small group of extremely intelligent people are responsible for the family of lisps. That tells me that if/when I do dedicate the time and effort to understanding the lisp tools it will be worth the effort.
(println (max 34 64 15))
becomes println(max(34,64,15));
and vice-versa.Another thing that helped is realizing that almost all things that require special syntax in other languages look like function calls in clojure. So, for example, to create a new function, you call the defn "function" and pass it parameters for the name of your new function, the expected arguments, and the body of the function.
The last thing is remembering that in clojure, the last statement in the body of the function is automatically the return value of the function, so I just imagined that the last statement had a "return" call in front of it.
With these 3 rules I could mentally translate 95% of clojure* to c-style code and back.
*The other 5% is mostly about macros, which are what give clojure (and other lisps) the power to add new features to the language via libraries, among other cool things. They they're powerful and used sparingly though, so you won't bump into them too much, and when you do, most will be documented as to how to use them properly.
I would say that most syntax in other languages is there precisely so that not everything looks like a function call.
In fact I think that only 13 "functions" are "magic" in clojure (as in written in java), and the whole rest of the language is written using those 13 functions[1]. That would be like, for example, adding golang's channels to ruby syntax by writing ruby code, without having to drop down to C, which i think is pretty cool.
I also found it very useful in real-life, mostly through having clojure libraries that can do things more seamlessly than similar libraries in other languages. For my last project, I needed to use maybe monad functionality quite often, and having it be as easy to use as the core language in my code was a huge win for my sanity.
[1]: I think some of the data structures are written in java as well, but only for performance reasons.
My real entry into the Lisp world was via Perl and the higher order functions that I used there. That enabled me to grasp the overall semantic of Lisp; lots of time in the code did the rest.
Actually if you're familiar with Haskell then Clojure idioms should be more familiar to you. Clojure's datatypes are (almost all) immutable, driving it towards a lot of functional idioms you see in such as Haskell or maybe Scala.
On the other hand, I would be genuinely surprised if someone familiar with Haskell were unable to pick up ML, or vice versa.
function doThis(arg1, arg2){
var m = arg1 + 3;
var n = modifyArg(arg2);
if (m > 3){
System.out.println("bigger!");
}
return m + n;
}(defn do-this [arg1, arg2]
(let
[m (+ arg1 3)
n (modifyArg arg2)]
(if (> m 3)
(println "bigger!")
)
(+ m n)
)
)I'm not suggesting this is good clojure, but it's closer to the way imperative languages are written.
One thing which still causes me to expend a few extra brain cycles for me is the [] in the let clause. The fact that the locally scoped vars m and n are contained within a syntactic block, as it were, makes me feel that they're within an inner scope and not available to the code below. It's really trivial and absolutely a feel thing, but it does have small effect on the ease of readability.
proc doThis(arg1, arg2: int): int =
let m = arg1 + 3
let n = modifyArg(arg2)
if m > 3:
echo("bigger!")
return m+n
With the added bonus that you get compile-time type checking (as well as a fair bit of type inference - notice I didn't have to specify any types other than the function signature), and the code itself compiles down to highly efficient C (https://gist.github.com/tylereaves/8116774 if you're really curious, do remember that code isn't intended to be human readable/modifiable). proc foo(x: int): int =
let z = x * 2
#z visible
block:
let y = z * 4 #z & y visible
let foo = 3 #z and foo visible, y out of scopeI think the answer is interesting because it's not purely mine, but instead summarizes a poll of ~5 strong engineers at my previous company. Two years later, I still think most of these bullet points hold, though I could also add a few more advantages and a few disadvantages of using Clojure.
2014 is going to be a big year for the JVM as the Java 8 gorilla cometh. Will be interesting to see what impact that event has on Scala/Clojure/Ceylon, etc. alternate JVM language adoption.
I use Scala in production, have done so for the last 2 years, have been learning a lot about FP and loved every minute of it. It's rather interesting, because before Scala I basically wanted C# on top of the JVM. And now C# is looking bland, bureaucratic, unproductive and I don't want it anymore.
Of course, many people hoped that some language will end up replacing Java as THE language for the JVM. That never had any chance of happening, even if closures would've never make it in Java. The people that wanted more capable languages already moved on and the shops and software developers that continue using Java will not change languages, because if Java worked well for them, it will continue to work well in the future and everybody had plenty of opportunity for change already. Java will still be the main language used in the enterprise, simply because enterprise software development tends to favor cheaper, easier to replace developers. Of course, this is one reason for why startups tackling the enterprise space are thriving, but that's another discussion.
Back to the point - we tend to think of languages and their evolution as some kind of football tournament. For me it's rather uninteresting what language will "win". I don't really care. All I care about is for a language to have a sustainable and active community that churns out useful libraries. And some people fear that they won't find jobs with language X. Personally I found the contrary to be true - finding well paying jobs for working on interesting projects in languages that are not Java, C#, C++ or PHP is much, much easier. Things are easier also from the employer side, because you've got less noise to deal with and usage of a certain language becomes one of the main attractions for that job. In my experience, it's a win-win combination, which is why I fear the thought of my favorite languages becoming too mainstream. But then again, I don't really care about languages that much. All I care about is for me to not suffer while trying to express what I want in code, which is why I stay away from Java.
Anyway, I'm on my Christmas Holiday, so back to reading "Functional Programming in Scala". It's a pretty cool book btw.
"perseption" => "perception"
"Fewer lines of code is a great thing" => [the opposite argument could easily be made as well; more lines of code (within reason) = less obfuscation]
(ns buttondemo.core
(:gen-class)
(:import [javax.swing AbstractButton JButton JPanel JFrame ImageIcon]
[java.awt.event ActionEvent KeyEvent ActionListener])
(:use [clojure.contrib.swing-utils]))
(defn create-image-icon [path]
(if-let [img-url (clojure.java.io/resource path)]
(ImageIcon. img-url)
(.println System/err (str "File not found:" path))))
(defn enable-buttons [button-flags]
(doseq [[button flag] button-flags] (.setEnabled button flag)))
(defn initialize-buttons [b1 b2 b3]
(doto b1
(add-action-listener
(fn [_] (enable-buttons [[b1 false][b2 false][b3 true]])))
(.setVerticalTextPosition AbstractButton/CENTER)
(.setHorizontalTextPosition AbstractButton/LEADING)
(.setToolTipText "I can disable the middle button.")
(.setMnemonic KeyEvent/VK_D))
(doto b2
(add-action-listener (fn [_] (prn "I told you not to click me.")))
(.setVerticalTextPosition AbstractButton/BOTTOM)
(.setHorizontalTextPosition AbstractButton/CENTER)
(.setToolTipText "Don't click me.")
(.setMnemonic KeyEvent/VK_M))
(doto b3
(add-action-listener
(fn [_] (enable-buttons [[b1 true][b2 true][b3 false]])))
(.setMnemonic KeyEvent/VK_E)
(.setToolTipText "I can enable the middle button.")
(.setEnabled false))
) (defn -main []
(let [[b1 b2 b3 :as buttons] (for [[title file]
; [["<html><center><b><u>D</u>isable</b><br><font color=#ffffdd>middle button</font>" "right.gif"]
[["Disable middle button" "right.gif"]
["Middle button" "middle.gif"]
["Enable middle button" "left.gif"]]]
(JButton. title (create-image-icon file)))
panel (doto (JPanel.) (.setOpaque true))]
(doseq [b buttons] (doto panel (.add b)))
(initialize-buttons b1 b2 b3)
(do-swing-and-wait
(doto (JFrame. "Button Demo")
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setContentPane panel)
(.pack)
(.setVisible true))))
)I count 19 special forms, 71 macros [1], 444 functions, and 29 variables in clojure.core. I don't think we should count functions or variables because these are like library functions and members (methods and fields) in other languages. But both special forms and macros are special in that they can't be passed around in the executing code. There's 90 of those, which is comparable to Java's 50-something keywords, or C#'s 70-something.
[1] excluding the 4 special forms `let`, `fn`, `letfn`, and `loop` which are also macros
Comparing any functional language to Java is just silly. As illustrated by the Fibanocci example.
lein new twitter
lein run
that's it.Most of the info in this article just reflects the rightful excitement form the author, but not in any way the best or the simplest way to do things. Just take it as "I love it, let me share" vs. "Clojure is better than..."
I don't agree with this. They seem "weirder" and harder, but they're not intrinsically difficult. They're just less familiar.
Also, there are a huge number of people out there who think they "know Java" but really don't. If you don't know what volatile and synchronized are and how they work, for one example, you don't really know Java. I would say that, unless everything in Java Concurrency in Practice is familiar to you, you don't really know Java.
Java and C++ are actually complex, difficult languages. The difference is that, with "design patterns" and explicit managerial attention to differences in ability (i.e. don't give mediocre programmers hard problems) it's more possible to half-ass that knowledge.