"Design Patterns are the disease, Clojure is the cure..."
blog.thinkrelevance.com
blog.thinkrelevance.com
After a positive experience with GoF patterns its hard to convince people that these patterns are just a natural part of working in lisp [or clojure] : Not only can you talk in patterns, but your code can be written in patterns in a direct way.
The same of UML and other formal diagramming systems - you get more expressive power much more directly via a 'domain specific language' in lisp. All the big fads are subsumed with a wave of the hand.
Unfortunately some universities take what they think is a pragmatic path of least resistance and teach popular languages such as Java in place of a much more subtle and powerful language.
I wonder at the sheer human waste in leagues of emasculated coders churning out verbose Java/XML/SQL 'business' apps in cubicles, forced by convention to work in an ugly way - those businesses would be much better served in terms of efficiency, quality and innovation by using a good language, and their staff much happier.
Thank Gods and the lords of Kobol for giving us Python, Ruby and Clojure that they might seep through the cracks and weaken the stranglehold of obfuscation and verbosity!
I have never bought this argument. What does it even mean? In every language, an implementation of a design pattern can be clearly distinguished as such. This is a good thing: it allows you to easily describe parts of of your application. This part produces objects, that part provides an interface to a bunch of other code, the other part determines which algorithm should currently be used, etc.
This is partly because lisps can act on their own expressions, transform them. This follows from extremely uniform or consistent grammar.
Now you could say C++ can do that, but look at the amount of machinery needed in Boost to add something like a lambda or an accumulator 'pattern' to the language.
Its a matter of degree - sure you can write a DSL in C++ [recursive descent, lex/yac, boost, macros etc] but in lisp this really is just a natural part of the language - syntactic sugar can be manufactured.
Lets take the idea of a callback - this is an excellent design pattern that gives you a great deal of power. You could implement using function pointers in C, or say functor objects with operator() in C++... either way, I contend there is a lot more crud and boilerplate than there is in lisp where a lambda is a fundamental construct of the language.
The more surrounding crud or machinery, the less clear the concept.
In other words, patterns in lisp can be _more_ clearly distinguished as such, because there is less cruft.
Often the name of the concept or pattern actually occurs in the code as a function name, for example.
Part of being a good programmer is being able to see through crud. Personally, I don't really care about crud. It doesn't bother me. I think it's a good skill to learn to be able to recognize and understand code that's within a bowl of crud.
Then you won't mind when a garbage truck unloads its multi-ton cargo into your home.
Surely the whole point of that setup is that they can get some productivity from mediocre programmers. Big businesses would rather have 10 mediocre programmers than 1 superstar programmer, and things like Java allow you to do that easily.
It's a good balance between power and not letting programmers make stupid mistakes. That's why it's so successful.
But Clojure has other niceties as well :)
I have been following Clojure with some interest lately. I'm still a Common Lisp guy though because Clojure is still in flux and evolving rapidly from what I see.
I also see the fact that all Clojure programs are Java programs as a plus and a minus: a plus because you get access to all the Java libraries and JITs and whatnot out there, but a minus because there can't be any independent Clojure port. In the real world to make an interesting port of Clojure you will also have to port all of Java so applications work. It's like if no one wrote Unix/Linux applications but only Windows and counted on Wine always being there.
With regard to state -- and Paul Barry already touched on this -- I recently started implementing a small app which needs some persistent state. I thought to try doing it in Clojure but found out that while Common Lisp has tools like Rucksack and Elephant and CLSQL, if I wanted it in Clojure I would need first to create all my data structures as Java objects (albeit with (. blahblah) wrappers if I wanted) then run them through some clumsy Java ORM, or deal with raw JDBC or Java Berkeley DB.
If I missed something that's already out there and available I would love to be pointed to it, but it seems to me Clojure could benefit from a native persistence mechanism or library that takes advantage of its concurrency strengths, or at least a native API for something like Berkeley DB that can deal well with it.
there's work on a clojure port on the CLR, u can check out more on the clojure website or the github project page.
"Java sucks, so use language X!". Problem is, this argument works for many values of X.
I think the elevator pitch for Clojure is more like: "Java libraries without Java verbosity". Or: "Lisp finally meets the real world".
(I've played with Clojure, and found a startling contrast in seeing Java objects inside such a friendly, dynamic environment.)
button.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked");
}});
vs defining a two line function then writing: (add-action-listener button #(println "Button clicked"))
Or maybe: (new Thread #(println "In thread"))IMHO, design patterns transcend language idioms and enable people to understand/communicate higher level abstraction.
The higher level abstraction aspects of a design pattern are usually better expressed without introducing codewords. E.g., the Observer pattern is just, "let's call a function on objects that want notification." It's the baggage that comes along with implementing these abstractions in blub that justifies a name as a pointer.
Related to the above, design patterns connote boilerplate; all that generic code that's needed to setup the pattern in blub.
The lack of an object system is a downright feature since it forces you to take a functional approach. Most of the things you would want from an object system (polymorphism, multiple inheritance) are readily available via multimethods/hierarchies.
If you really want something CLOS-like, there's a library for that, http://code.google.com/p/explorersguild/wiki/XGModelAndGF.
I don't think Clojure is really a functional langauge, it's more of an imperitive/OO mix with immutable objects. Immutable objects aren't "functional programming", they're simply common sense.
Functional programming is about composing parts of programs ("functions") with generic combinators. I have not seen anyone in Clojure doing this. Haskell is a different story; combinators are very reusable, and there is no way to combine functions (and other "categories") without them. (In Clojure, you combine program fragments by typing a closing paren and then opening a new one. Your program doesn't control its sequence of operations, a Magical Runtime does that for you. Inflexible.)
The reusability in Haskell is quite interesting; for example, application in a monad is defined as "liftM2 ($)", the usual function application operator ("($)", itself a function of two arguments) lifted into a monadic context for functions of two arguments. Cool.
The reusability is everywhere; you use the same Arrow combinators to traverse an XML document as you do to parse plain text. (See HXT and PArrows.) And of course, you compose Arrows with (.) (the function composition function), because (.) works on all Categories, not just functions.
That is functional programming; Clojure is just immutable data and parens. (Both good ideas, mind you. But it's not doing anything that Pure Java can't.)
"A programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data"
Wikipedia goes on to consider Erlang and Scheme functional programming languages, so it would seem your definition of what consists a functional programming language is more narrow than the norm. Most people would consider Clojure to be a functional programming language, just not a pure functional programming language.
I'm also not sure why you describe a language with macros as being "Inflexible". I've used Clojure and Haskell extensively, and Clojure wins on flexibility. It's a dynamically-typed Lisp, after all.
Wikipedia goes on to consider Erlang and Scheme functional programming languages
Yeah, people have diluted the term to near meaninglessness.
Sounds like you need to get out more.
I decided to take a look at your Clojure code on Github, and I am not seeing anything functional. Lots of OO/imperitive things going on; global variables (all instances of your HTTP client share the same cookie jar?), mutating objects, standard "imperative" control-flow (try/finally, with-<foo>, etc.), and so on.
It looks basically like Emacs Lisp but with fewer parens in "let" clauses and with "defun" spelled "defn".
That is not functional programming.
You don't mention where you saw all the "global variables", but I'll bet that what you saw were actually vars used in conjunction with binding.
Are we really to the point where only monadic IO is acceptable to the high priests of functional programming?
FP has always had two parallel tracks, the ML school and the lisp school. Maybe you prefer the former, but just because you really, really like Haskell doesn't mean you get to bar all other lisps from the FP club (as all of your problems with clojure -- i.e. it not being haskell -- certainly apply to all lisps I've worked with).
But calling a bunch of mutable state and non-composable program fragments that run from top to bottom is not really functional programming, and calling it that just confuses people. The idea of language is to be able to speak clearly about something; having a nebulous expression with no meaning is not helpful, right?
Again, it sounds like you think any given scheme code isn't functional programming, either, it not being lazy by default, etc. Small world, that.
It's cool that you're expanding your horizons by reading code in a new language, but if you hold off on snarkiness until you actually understand the code you're reading, (particularly with regard to what the binding form does) that would be great; kthxbai.
As for what a real object system is... does this even mean anything?
This is precisely why snark doesn't work. Snark isn't based on substantive analysis and argument. Instead, it's the same sort of stuff that had lots of us geeks ostracized in high school. It's the hipster/school clique side of human dominance and group-think instincts.
It's the reason why such inaccurate yet ineradicable notions continue to circulate in the programming field.
If one considers oneself a thoughtful computer programming expert, leave out the snark!
And yet you like Perl?! ;-)
I don't want to say something like, "do you know who I am", but you maybe click the links in my profile before trying to convince me of Perl's merits :)
All the other links seemed to be announcements for the guy speaking and article itself had no real meat. Show me some code does something hard easily and something easy, easily. And don't make have it be factorial, I know functional programming can do factorial.
Have a look at PGs 'on-lisp', 'the little schemer', the SICP book + video lectures, or 'practical common lisp' - they have example after example - finding fixed points, data query language, numerical approximations, symbolic algebra, pattern matching, logic languages, momoizing all expressed very clearly.
An excellent article you might enjoy is 'Functional Programming for the Rest of Us' : http://www.defmacro.org/ramblings/fp.html [by a YC alumni, Slava]
I might regret admitting it but functional/dynamic programming is new to me, but I'd love to learn more about it. Groovy and Clojure sound pretty similar to me, minus the Lisp part, so I'm curious what some of the other major differences are.
From what I've read, Groovy is monstrously slow. That alone should rule it out.