Why I am excited about Clojure
blog.txus.io
blog.txus.io
Really? This is such a big problem that you have to choose a completely different language.
Edit: Improve clarity.
One wonderful thing about Haskell is that there isn't a cost to using let expressions, even if some of the values will never be used. Laziness means I can freely decompose my complex expressions into clearly named components and not worry about doing unnecessary computation.
Any language can be written poorly. I don't think any language will ever be able to force good coding style on programmers. Though Haskell comes closer to that impossible goal than many other languages, due to its type system, functional purity, and mandatory indentation.
Well, technically it's possible if someone used macros to embed a DSL that is paren free, but it just doesn't happen in the vast majority of cases. Whereas if you try to follow the approach of using parens everywhere in Haskell, the vast majority of other code you attempt to read will still be awkward for you.
Having to explicitly indicate the precedence of operators -- as in Lisp's prefix notation -- completely eliminates precedence errors, both in writing and in reading. This is true even though "well-written"^1 code using implicit infix notation is readable.
[1] Note also that the level of implicit precedence that you're comfortable with might be different from mine.
* /
+ -
= !=
&&
||
Of course you can always use an infix macro in Clojure, e.g. http://data-sorcery.org/2010/05/14/infix-math/ quote do
1 + 1
end
returns: {:+, [context: Elixir, import: Kernel], [1, 1]}
which, if you squint at it, starts to look a little Lispy.One thing that has put me off on Clojure is 1) the ugly as hell JVM stacktraces, 2) hitting the wall of the number system results in nasty JVM errors if you are used to Ruby's trivial (to the developer at least) handling of numbers of any size, 3) the time it takes to fire up the JVM itself (I like really fast really instant unit tests)
On the other hand, what has put me off on Haskell is that 1) all its users seem like mathematics professors, 2) a lot of its libraries will fail to compile when combined with each other without a lot of hand-holding and carefulness
1) Stacktraces in JVM may be ugly, but the amount of insight into runtime issues that the JVM offers you is generally way higher than in runtimes like cpython or MRI.
2) Clojure gives you a rather elegant way to work with the different JVM number types as it offers literals for those. I think Java Longs (the integer default) should be long enough as it's a 64 bit value. And on the floating point side the default is a double, which also seems nice to me. See: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/dat...
Above this Clojure offers BigDecimal literals and native ratios.
3) The startup time is gigantic. However, while developing you usually should not have to restart the JVM as you can update the JVM with new bytecode through the REPL. This should also dispel the concern about unit tests (although I'm not sure you can reach the necessary speed, if you talk about < 0.5 second unit tests, because the code needs to be compiled to JVM bytecode 1st. There's definitely still potential there.
Does anybody know if something like that is possible/already exists?
http://www.martiansoftware.com/nailgun/
http://leiningen.org/grench.html
All 3 not perfect, but have a look :)
The majority of commercial JVMs that tend to be ignored on HN, provide JIT cache between runs and AOT compilation.
3) You don't pay the price for the JVM startup time when running unit tests. When developing Clojure you have a JVM active at all time for your REPL. The JVM startup time is really only a problem if you want to write scripts.
Your first issue with Haskell is a plus! Everyone in #Haskell on freenode are incredibly helpful, and well, if they're all professors you likely to get some quality help, right?
Eh... The Haskell community is amazing and always willing to help. They get an A for effort, but they often have the curse of knowledge[0].
> The curse of knowledge is a cognitive bias that leads better-informed parties to find it extremely difficult to think about problems from the perspective of lesser-informed parties.
After working a while, I finally give a clear advantage to Haskell. Yes, Math people are chatty in Haskell, but you can do _a lot_ with very few Math.
In fact, with Haskell, the more your project growth, the more time you spend on interesting things. While in Clojure (and most dynamic languages), you'll lose your precious time with undetected syntax errors, and all standard unitype errors.
But for _very_ simple projects, Clojure is great in term of productivity.
So in general, I try to use the right tool for the right job.
- If you want to make a dirty thing, use a shell script or Perl. - If you want to make a prototype, Clojure is great. - If you want to make a project for production, then Haskell is the clear winner here (in term of bugs/efficiency/time).
One thing I get with Haskell I didn't got with Clojure is "code scalability". In Haskell, my code tend to be more reusable than in Clojure.
Clojure sits on top of the JVM, and as such, has the benefit of interop with some of the most robust software packages in the world.
Unless I'm missing something, you'd need to roll your own packages in Haskell for all this functionality.
I would argue that the most efficient code is well maintained open-sourced code that you don't have to write.
I don't use Haskell, so there may be lots of libraries I don't know about, so forgive me if this question is naive.
I was sold in part by the JVM argument. But in reality, in practice, I found it to be more a burden than a delight. First, the stack trace errors. To deploy your application, it is not just a "copy your jar in Tomcat". You have to serve using Clojure. So in reality, you don't get all advantages provided by the JVM environment, only some.
Clojure is fast, but, Haskell is at least as fast as Clojure.
Up until here, I had all packages I needed. There are all here[1]. I made mostly Web oriented programming, but using MongoDB worked extremely well for example.
And the most important aspect (from my experience):
When your program growth, in Clojure the time you spend to add another feature growth rapidly (it is better in clojure than most other languages thought), but in Haskell, each new feature doesn't cost so much, even in a medium to big project.
So when you need to change something fast without breaking something (the without breaking something is the important part), nothing beat the Haskell type system (or may be Idris, but I didn't played with it enough).
Furthermore, the ability to minimize the number of bugs, gives me a good feeling about the quality of the libraries. Of course, Haskell is not magic, you still have to pay attention to bugs, to test your code. But Haskell helps a lot in not falling into dumb bugs.
This isn't really true.
Yes, directly using a Java library forces you to use Clojure that is not idiomatic but 1) this isn't always the case 2) there are idiomatic wrappers over some libraries already.
More importantly, at least you have the option of using Java libraries. Having to use Clojure code that is not idiomatic still will be much more productive than writing the library yourself in Haskell.
In a production server I'm running right now, I've used three java libs, wrapped them very nicely and neatly, and they work great.
> I was sold in part by the JVM argument. But in reality, in practice, I found it to be more a burden than a delight. First, the stack trace errors. To deploy your application, it is not just a "copy your jar in Tomcat". You have to serve using Clojure. So in reality, you don't get all advantages provided by the JVM environment, only some.
For my current app, I just need to do: lein ring uberwar, copy to tomcat directory. done.
The rest I can't speak towards as I don't know Haskell. Also, it should be noted, I'm only offering counterpoints because I don't want people to not use clojure because of some of the incorrect claims. I guess we've had different experiences with clojure. :)
Clojure has a beautiful dynamic type system. Haskell has a beautiful static type system. If you're working on a project with changing requirements (which for me means most nontrivial applications), Clojure will be much easier to work with and, as with most Lisps, is very well suited to what pg calls exploratory development. If you're working on a project whose requirements are largely static and known in advance (compilers, interpreters, parsers, static analyzers, mathematical tools, etc.), then you won't have to worry about coding yourself into a corner and Haskell's insanely powerful type system becomes a positive.
Oh, definitely. I just feel that far too many people focus on programming languages that are good for beginners without really thinking about them from an experienced programmer's perspective. What may be good for a beginner can often be terrible for an expert. What may be hard to understand for a beginner can be extremely powerful and simplifying for an expert.
I think the question people should ask themselves is: "Do I want to stay a beginner forever? If not, why am I using tools that are designed for beginners?"
We regularly extol the virtues of expert-friendly text editors, but don't talk much about the appeals of expert-friendly languages and frameworks. Also worth thinking about: a huge amount of hype gets allocated towards beginner-level languages/frameworks that solve beginner-level problems (todo lists) more easily.
Edit:
You want to take the next step that has enough familiar aspects that you can acclimate reasonably quickly and build non-trivial programs (the fun part) in a reasonable amount of time. But also different enough that you're exposed to new concepts and new ways of thinking about programming, and also to have access to a more powerful language.
Simply diving head first into a powerful but completely foreign language might not be the best way. Coding can be as frustrating as it is rewarding. And there's something to be said for trying to maximize the rewards and minimizing the frustrations.
There's also a lot to be said for habit-forming. Imperative languages teach you a lot of habits and cause you to develop assumptions that might actually make it harder to learn a language as different as Haskell. This makes the concept of "taking steps" in your growth as a programmer a nonlinear one. Some languages might even hurt your ability in the long run.
If we were even half experts, maybe that would change, but in a field where "anyone can be a developer" you will see a long tail of low skill.
Industry routinely degrades skill and experience in favor of "just ship." New frameworks are continually churned out with the implicit promise that they'll fix everything (except their own lack of composition). Custom development is viewed as expensive when someone else can come along and paste several libs together, then apply duct tape to hold it all together. Domain modeling is viewed as esoteric. Testing is still hotly debated.
...I don't want to go on.
https://github.com/jwiegley/github/issues/25
Experienced and skilled developers (wiegley's open source work includes ledger and great contributions to emacs org-mode), and the task is to make the API client's base URL configurable. Should be trivial. And yet it descends into farcical stuff about whether they need to introduce a new github monad or to "abstract over a type class". It seems like the purity was really getting in their way. The issue was never solved. I may be cherry picking an example which serves to parody Haskell, but for me that example is really off putting.
One is due to it's declarative nature (which I love), it's quite difficult to get up to speed with unfamiliar code.
The 2nd is due to it's JVM roots. Those stacktraces, man. And when it happens on an anonymous funtion in a not so documented library, ugh. It's like hitting a brick wall.
The first, I can deal with enough practice and Schema/core.typed. Is there any hope for the stacktraces to improve? How do other JVM based languages fare in this regard?
(fn my-name []
(throw (Exception. "boom")))
(Sorry, I don't know Clojure internals well enough to have great answers to your explicit questions.)[Edited: thanks to jerf for pointing out the inconsistency in a term I used.]
i.e.
(= ((fn factorial [x]
(if (<= x 1)
1
(* x (factorial (dec x))))) 5)
120)
Note, this isn't run under TCO (see loop/recur).You can make a code change and refresh the browser and your changes are reflected.
Not being able to compile a program to check if the changes you made are even coherent is also a nuisance.
Also, nothing says that compiling a program has to take a long time (eg. go).
(alembic.still/distill '[org.clojure/tools.logging "0.2.1"])
Or something like clojure/tools.namespace[2] to edit your project.clj and then reload/refresh all namespaces to be current.
[1] https://github.com/pallet/alembic [2] https://github.com/clojure/tools.namespace
Ruby and Python borrow a lot from Lisp, including the REPL, but some of the most popular applications of those languages barely take advantage of it. Why?
[0] I'll stipulate that you might want to be very conservative about doing this with a production system.
No kidding. I knew at least one guy who hosed a product launch by doing that. You even have to restart your development instance from time to time to make sure that your application state remains consistent. I've been scared to try, but I suspect that you could live-patch a server though if you were systematic about it and tested the patch ahead of time.
There are ways around that. Stuart Sierra uses a scheme wherein the entire application is treated as a value, which can be re-generated on the fly. Of course, that's not terribly different in practice from restarting the whole environment, but it's faster and more convenient.
http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-r...
There are technologies deigned to enable live updates. VisualWorks Smalltalk had one, though it was not widely used. There are real time OS that can update the kernel without going down.
Imagine having a REPL to your complete OS, not the just the basic CLI that most systems have.
There are a few videos of those days showing up the systems.
My experience with Oberon, lead me to track down how Wirth learned about such systems.
Additionally, I got to learn Smalltalk in the form of VisualWorks before Java was known to the world.
So I extrapolate my experience to how it might have felt back then and find it sad we are still trying rediscover it.
I do lots of small functions that build on each other. If I make a change to one of the base functions, the higher functions seem to hold a reference to the old definition and I end up having to re-eval everything or go figure out every place I used that function.
I'm sure there is a better way but I haven't found it yet. I've tried using some ideas from Stuart Sierra about reloading namespaces, but I spend more time trying to orient my code to work with their ideas than just getting something done.
And documentation in clojure code in general is very poor. People think their code is self documenting. It is not.
While clojure is wonderful to write in, it's not nearly as enjoyable to read. The more macros and syntactic sugar people add to their programs, the less likely someone is going to be able to read, understand, and use that code.
Also, redefining a function should cause every function which directly references it to use the new version. You can sometimes run into issues if the functions are defined in separate namespaces or if the redefined function was passed to the other as a higher order function during the latter's definition.
If it's a macro you're redefining, yes you'll have to redefine functions which depend on it, but you should be defining too many macros.
Well, I think macros often play the role of jargon. They let you spell out a particular concept succinctly and precisely and make the whole much easier to follow once you are familiar with the jargon, but people unfamiliar won't fully understand.
Much like jargon in spoken language in any field it can be picked such that those otherwise familiar with the language draw conclusions (that are hopefully correct), or picked so that they recognize that they are unable to draw substantial conclusions (which may be incorrect). There are upsides and downsides to both approaches (which isn't to say one doesn't dominate, but from my limited perspective it's not clear).
I should note that, to some extent, this is true of most kinds of abstraction.
Macros can be used to implement some pretty novel abstractions so there can be some temptation to try to understand them by reading the implementation. However, if an abstraction is novel enough, it really should be documented somewhere very thoroughly.
So, to some degree your analogy makes sense. You can use macros (and functions too, really) to express concepts that are unclear unless you have a better familiarity with the topic or the program in question. But I feel that that's what docstrings are for.
Clojure functions and macros have docstrings you can query with the "doc" function. Honestly I've started keeping a repl open all time like I used to do in C with a terminal + man, so I can use (doc whatever) or (source whatever) to see the documentation + implementation details of most of the functions I use.
To love clojure you'd have to first become proficient in it and I highly doubt even double digits of HN would claim to be proficient, much less to love the language.
In general, you speak for yourself and yourself alone and my take from your comment is that you love clojure.
I have played around with it but not enough to be able to say that I love it, even though I would like to spend more time with it.
I'm sure that when the honeymoon phase is over clojure will be yet another useful tool in the toolbox. I can't recall a single language that I truly love, they all have their specific warts and I expect clojure to be no different in that respect.
Just a different set of limitations to applicability.
I do not think that the GP was using 'love' in that sense. You can 'geek out' about news and information about something without actually being much involved in that something at all. Witness the popularity of posts about space launches on HN. I doubt that there are very many actual rocket scientists, but we sure do have a lot of people interested in progress made in space.
I'm pretty sure many times more than 99 HN people have used Clojure extensively. Heck, there have been posts here from teams using it in production on their startups.
From Matz (creator of Ruby):
Ruby is a language designed in the following steps:
* take a simple lisp language (like one prior to CL).
* remove macros, s-expression.
* add simple object system (much simpler than CLOS).
* add blocks, inspired by higher order functions.
* add methods found in Smalltalk.
* add functionality found in Perl (in OO way).
So, Ruby was a Lisp originally, in theory.
Let's call it MatzLisp from now on. ;-)Genuinely curious. If I'd had to guess I say just the spirit of the language, the macros being reincarnated as the highly dynamic smalltalk method system. The s-expressions being reincarnated as the blocks.
...otherwise they'll just go "yuuuck!" or "wtf?" or "this is weird" and walk back away to their cubicles. Ruby is great because it changed the culture by letting the kinds of people that wouldn't have dabbled in functional-ish programming or DSLs put their toes in the meta-water ...now hopefully, some of them will start to take swimming lessons :) (and a few will drown themselves and their friends, of course, but there are prices to pay for enlightenment)
java script :)
see also: https://mxr.mozilla.org/mozilla/source/js2/semantics/
* Start with Emacs Lisp [1]: Well, if you like, but in exchange for the perceived simplicity as compared with e.g. ANSI Common Lisp, you're also giving up extremely useful things like lexical scope, and that's going to hurt your users down the road.
* Remove macros and s-exprs: Why? They're a powerful feature, and if you're building a Lisp, then operating on the AST shouldn't be hard. Except that removing s-expressions costs you homoiconicity, which means you need to translate from source to AST, which means the difficulty of writing a macro just jumped by several orders of magnitude, so, fair enough, if you're throwing out s-exprs then macros have to go as well. I can see an argument for this decision, in that it lowers the barrier to entry for non-Lispers who have yet to get over the parentheses allergy.
* Add simple object system: Is it really that much simpler to do single inheritance + mixins (Ruby) than to do multiple inheritance (CLOS)? This is an honest question; I've never implemented an object system, so I don't know which is easier from the developer's perspective. From the perspective of the language user, though, I don't know that I see a lot of difference.
* add blocks: This decision strikes me as totally indefensible.
If you already have lambdas, why do you need this separate abstraction which looks and smells mostly like a lambda, but doesn't act like one except in certain circumstances, and which is also the only place in your entire language with lexical scope rather than dynamic? You've just unforgivably complicated your language's syntax and implementation, and your users' lives; you've made it necessary for people both to write, and to understand, documents like [2]. Why would you ever do that?
And if you don't already have lambdas, in what crazy world of paisley skies and ice-cream seas does it make any kind of sense to put yourself and your users through all these unnecessary mental calisthenics, when you can just add lambdas, and solve all these problems with a single simple abstraction that everyone either already knows or should?
I can't imagine how anyone could possibly defend this reeking farrago, but I'd sure like to see someone try.
* add methods found in Smalltalk: I'll concede this one; it was a good idea, and a lot of Ruby's power comes from being able to hang magic off method_missing. Of course, a lot of Ruby's headaches come from that, too, and it is a persistent source of subtle bugs and unexpected behaviors, especially in frameworks like Rails which make heavy use of such magic hooks. For Smalltalk developers, this isn't so much of a problem, because Smalltalk is so heavily self-documenting; Ruby developers are not so fortunate.
* add Perl functionality: Well, this makes sense, if you're trying to come up with a Perl killer, in the same way that Perl was explicitly intended to be an awk and sed killer. It seems lately to be working, too; there's what looks to be an increasing trend for people to use Ruby for the sort of tooling which, a decade ago, they would've done in Perl. On the other hand, and I say this as someone who grew up with Perl and will always have a soft spot for it, Perl is a giant ball of hair laced with fish hooks, into which only a true expert may plunge his hands and pull them back out without severe lacerations. If you let your Perl emulation anywhere near the conceptual or implementation core of your language, you risk infecting it with the same disease -- indeed, I'm inclined to wonder if that's whence came the brain damage that led to Ruby's mess of mutant lambdas.
Don't mistake me: Ruby is a cute, useful little language, and it has some interesting ideas which I think are worthy of wider consideration, such as the method system. Unfortunately, Ruby also has some grave drawbacks, such as the lack of a self-documenting capability to make the method system sane to use at scale, which in my opinion rather severely reduce its appeal.
[1]: No, I've never seen Matsumoto admit outright that he started with Emacs Lisp, but I think it's a reasonable surmise. Consider: Emacs Lisp descends from MacLisp, which is both simpler and older than Common Lisp; in both Emacs Lisp and Ruby, lexical scope is an afterthought, inflicting at least occasional headaches on users of both languages; prior to inventing Ruby, Matsumoto was an Emacs Lisp developer of considerable Japanese repute.
[2]: http://awaxman11.github.io/blog/2013/08/05/what-is-the-diffe...
The same applies to Clojure. There's laughably little syntax to learn, and once you've got that under your belt you're just composing functions.
I'm not made to feel I've wasted a lot of memory space on the esoterics unique to some particular language. Clojure works for me rather than the other way around. This no-nonsense obviousness was one of Matz' design goals for Ruby, and clearly ditto for McCarthy and Hickey.
That's homoiconicity, and Clojure strongly resembles an actual implementation of this conceit.
As for differences I like Clojure because I can compile up an uberjar and hand that uberjar to ops and walk away, so its faster and easier, whole regions of "fun" (In the dwarf fortress sense of "fun") that exist in ruby deployments simply don't exist as a class of problem in clojure deployments.
* immutability by default
* decoupling by default
* small libraries rather than mega-frameworks
The comparison is not quite fair; but there's something to be said for appreciating a well-engineered solution versus the usual "SHIP FASTER, PEON!" schtick that Ruby has.
My opinion is that it crosses a syntactic threshold of abstraction, after which a language just loses. s-expressions are extremely powerful, but also the language's Achilles heel.
This resembles how there is a 'happy medium' for writing human languages. We could write all of our text in morse code, but we don't. We know that too many glyphs, like in Chinese, slow down learning too, as the effort to remember them all takes lots of practice.
So just like Scalaz's operators are so arcane that us mere mortals are better served by using words instead of <=o=>, a world of s-expressions makes it harder to find your way precisely because of excessive syntax homogeneity.
Still, I think Lisps are worth learning, it's just that I think the ideal language steals much from Lisp, including most of what s expressions are used for in practice, but it doesn't go all the way.
Ruby appealed to me because of its simplicity and conciseness, but after a couple years it turned out to actually be rather complex.
I found the simplicity and conciseness I was looking for when I was randomly reading some Clojure code a couple years ago in the speclj sample docs (http://speclj.com/tutorial/step7) of all things.
I'd also like to do some code reading. I know the Clojure source code is on GitHub, but maybe something smaller to start?
If anyone else is interested in getting started, here are a few links I visited over the weekend:
http://www.braveclojure.com/do-things/
http://aphyr.com/tags/Clojure-from-the-ground-up
The last link is an hour-long interview with Rich Hickey - a fantastic introduction to the language and his design goals. It's probably the best technical video I've ever watched on YouTube.
This is a very weird paragraph to me because I've experienced the exact opposite. You can see my frustration in this stack overflow question I titled "How do I get better feedback from Clojure errors?": http://stackoverflow.com/questions/16901836/how-do-i-get-bet...
While I agree that JavaScript has tons of "Wat" quirks that shouldn't be there, I rarely run into them. And more importantly, when I get errors in JavaScript, the cause is relatively easy to track down.
With Clojure on the other hand, it is really difficult for me to figure out why I'm getting the error. Yes, I get the error because I "was doing it wrong", but the errors don't help me figure out what I was doing wrong.
-------------
> With Clojure your editor (be it Vim, Emacs, Light Table...) is permanently connected to a live REPL. You continually develop, test and modify functions with subsecond feedback. Continuously. All the cores in your brain are lit, as you have literally no time to think about anything else. That's not only deeply satisfying, but also leads you to certain thought paths that slow feedback and its inevitable lower focus would have simply blocked. That's one of my favorite parts of Clojure.
This is very useful when you're writing new code, but I found myself scratching my head when I had to go back and modify already existing code to add a feature. As a workflow, I don't "get" when/how you're supposed to add automated tests when you're using REPL driven development. But, once I went back to my code, I always wished they were there.
You see, I'd modify the code and break something and then the problem I described above made fixing it very time consuming. Usually the cause of my problem was my simple functions wouldn't integrate together the way I expected. The REPL gave me the quick feedback to say that "increment-number" worked, but I would accidentally pass in a String or a vector into the function.
As a bonus, the UX to ghci is better than using nrepl/cider in Emacs. Reload "just works" and works more quickly than reloading namespaces in Clojure. I was gobsmacked when I saw how much better ":r" worked in ghci than reloading in the Clojure REPL.
Where as in clojure, the scope of name spaces is generally smaller, while the errors are more cryptic, I agree. The line number that it points you to and use of the repl allows one to dissect the function in great detail and by checking the incoming data and outgoing data, the bug becomes much clear. At least in my experience.
Can anyone explain or point me in a direction where I can learn how the above functionality is actually used and useful?
I keep hearing about live-editing/hot-reloading, and in theory I believe it must be very useful, but I cannot concretely imagine how it would be used in practice. What kind of programs can work like that? Continuously running programs, like web servers/GUI programs? So what happens when you change a function? Do you manually simulate a web-request? Rerun unit tests? Would it work with command-based pipelined programs, such as compilers as well?
You can also mess around with transforming state that you may have stored in an atom...
I played with Clojure making a simple pong game, and used this to work on collision detection. I worked on a function called rect-intersects that took in two rectangles. It was much faster to work on than if I'd booted up the game each time.
I use Common Lisp and Hunchentoot to build RESTful web applications. I can connect to the Lisp image running on the server using Swank from Emacs on my development box. If there is a bug, I can change the function, recompile it, and test the change with curl, without rebooting the server or reloading the app. The same techniques work for adding new functionality and APIs.
I suggest just giving it a try. It'll change the way you think about building systems.
This is really appealing to me. I've hacked a bit with Ruby (One Month Rails and some other tutorials), but I'm not sophisticated enough to know instinctively how to refactor towards "clean" or "elegant." Most of my code is pretty ugly, and gets uglier the longer I work on it, since when I'm hacking on something without guidance I spend a lot of time just trying not to break things. Is there a good resource for a non-programmer to start learning Clojure? The syntax is a bit scary to me, but I just think back to when I first learned about programming and figure diving in will eventually make it read more sensibly to me.
Also, being connected to a live REPL seems pretty valuable for learning.
edit: Realm of Racket is also a good lisp book, and a lot of people like "the little schemer". It's hard for me to say how good these are for people completely new to programming, though.
Finally, there's the ClojureBridge curriculum, which is targeted at new programmers: https://github.com/ClojureBridge/curriculum
Clojure backs off the 'referential transparency ONLY' philosophy, and merely discourages mutability. In my (limited) experience, this gives almost all of the advantages of Haskell, but an environment that's easier to program in.
So yeah, I'm excited about Clojure too. I just wish we could port it off the JVM...
> this gives almost all of the advantages of Haskell
except one of the biggest ones - the type system. Obviously this is subjective though. Some people prefer dynamic typing :)
I've done C and Java at university, but that was a while ago.
Anyone has a comment?
I got some good perspective with https://www.coursera.org/course/proglang and you could do worse than taking grossman's excellent course. It helps to have some structure and a schedule when starting imho.
I will admit at times I wish I had more chops (closer to the metal i.e. C or C++), but I think I was eventually just more interested in new ways to think about programming, and I'm not sure I'd get the same pleasure out of digging back into C++.
Obviously, if you know something more about your intended use case (i.e. you want to program games), you can make a more informed decision.
Yes, this is my exact feeling when I'm still learning it :)
The parentheses can decrease readability and the prefix math expressions can make the order in which you think up a solution awkward.
http://sourceforge.net/p/readable/wiki/Problem/
http://cs.brown.edu/~sk/Publications/Papers/Published/mfk-va...
1) Easier to learn and remember syntax for polyglots. No more Googling, shit like "what was the syntax for try-with-resources or the new multi-catch feature in java"?
2) The language tooling is so much easier to get right. The parser for Ruby is 10k lines. What the actual fuck? How can you expect people to make good tools if they can't get past the parse step?
3) Macros!
4) No fucking precedence rules. I don't want to memorize that the precedence for 'and' and && is different in Ruby and how 'and' interacts with the precedence of other operators like assignment.
5) Structural editing.
I might have forgotten a few, but 2) and 3) alone are killer features.
Edit: Elixir [2] is homoiconic as well, with even more uniform syntax (though it doesn't have special matrix syntax).
The only time it becomes a major issue is if you are nesting many functions, but in that case the threading function is both utilized often and quite elegant.
Instead of:
(fun3 (fun2 (fun 1 "string")))
(-> "string" fun1 fun2 fun3)What's more, because one can macroexpand on the fly, you can actually see the code that it transforms into.
That realization may not make the code any easier to read at first, but it may make it easier to accept it.
Try solving a small problem in Clojure (a code Kata, or a problem from Project Euler) and you'll see those aprehensions against the parens will go away.
Nowadays I'm having trouble to accept the syntax of other languages, eg. Scala :-)
The parens are annoying, until:
a) You build that fully composable library that you always wished you could have written in X language, but it neeeeever quite worked the way you wanted.
b) You realize that by keeping your data immutable, it allows you to write less tests, be more confident in your code, and you stop worrying "is that value is what I think it is?"
c) By building on top of the JVM, you are able to use java interop to save yourself a day of coding a custom library for something that exists and is well tested.
d) Deployment becomes a breeze because you just export a jar/war file and load it up into any of the existing app servers.
e) You get phenomenal speed increases for "free" if you're coming from dynamic languages like ruby/python/PHP
f) When you need to dip into async code, you can write your async code, in a synchronous fashion, which (for me) is much easier to think about then keeping track of callbacks in my head.
Good luck, if you decide to give it a shot, I think you might realize the parens isn't such a big deal in the long run!