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.
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.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.
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.