Haven't messed with a LISP language since college but clojure has grown on me considerably. I already like it more than Scala... though I did almost immediately find myself reaching for a monad library (cats seems good so far) :) Haven't felt the need to write my own macros yet, but certainly many of the libraries I'm using (compojure) make heavy use of them.
Too bad my day job is all Java, all the time :( My continual requests to use Scala have been shot down over the last couple of years -- "We want something maintainable, where am I going to find Scala devs when you leave?" So obviously Clojure isn't going to fly :)
As an example, I've been using GraphQL quite heavily for the last 8 months, primarily with JavaScript and i've found it to be a fairly tedious experience. I tried out an Elixir GraphQL library that's heavily based on macros and was almost immediately more productive - despite not even knowing the language.
Similarly, coming up with a nice API for working with forms seems to be pretty much impossible with JavaScript. I've evaluated dozens, and not one comes close to the simplicity of Django's forms (which is only possible thanks to Python's metaclass magic).
Having sampled the power of languages with macros, I'm naturally drawn to Clojure, simply because it has a very decent JavaScript implementation. I'm sure there are other very good reasons for using the language, but right now macros are the motivation.
I'd like to understand why one would need 10 years of lisp experience before starting to author their own macros, it feels like a philosophy that's just going to stop people trying out lisp.
Also, given that Clojure is a lisp, I think you'll find you need macros left often than a language like elixir. They're still incredibly powerful, but you can do so much in Clojure without them that you would otherwise need a macro to do.
I guess i've just heard for so many years how badly wrong macro usage can go, that i've not even been tempted to try languages that use them - until very recently.
However, looking back at Lisp/Clojure now I really don't like the syntax anymore. I don't care about the brackets, it's absolutely true that you learn to look past them in a matter of days, but the homogeneity and the "denselesness" really impact my ability to quickly parse an expression. Infix syntaxes make reading code much easier, imo.
1 + (2 * 10) - (12 / 3)
is so much easier for me to read to read than (- (+ 1 (* 2 10) (/ 12 3)))
But when working with functions, I much prefer the standard Clojure syntax. (- (+ 1 (* 2 10)) (/ 12 3)) ;=> 17
I've found the threading operator can help in these cases. (-> (+ 1) (+ (* 2 10) (- (/ 12 3))))
Or, line breaks can clarify: (- (+ 1 (* 2 10))
(/ 12 3))
or at the risk of overdoing it: (-
(+ 1 (* 2 10))
(/ 12 3))
Now I know at first glance that I'm subtracting the results of those two lines from each other.
Indeed the last two might be almost as clear as the infix, and are without any order of operations rules.Also some don't like colored parens but I find them very clarifying.
f x . g
reads just so much easier than (comp (partial f x) g)For me it's mostly centered around control structures - if, for, switch, case (scala's pattern matching). Clojure's control structures are syntactically composed of ()'s and []'s. Scala's aren't. They are a coded into the language's grammar[1][2]. Clojure's ns forms parallel Scala's package and import statements, but are again Clojure's are syntactically composed of ()'s and []'s. I know it's a relatively small point, but control structures make up a significant portion of programs.
As a side note, I took a look at a Clojure grammar also specified for antlr[3]. My initial thought would be that Clojure's grammar would be appreciably smaller. It is, but not by as much as I was expecting. There are a lot of very little additions to the reader that add up [4][5][6].
[1] - Scala's syntax http://www.scala-lang.org/files/archive/spec/2.11/13-syntax-...
[2] - Examples https://github.com/lrlucena/grammars-v4/blob/master/scala/sc...
[3] - https://github.com/laurentpetit/ccw/blob/3738a4fd768bcb03996...
[4] - http://clojure.org/reference/reader#macrochars
[5] - https://yobriefca.se/blog/2014/05/19/the-weird-and-wonderful...
[6] - See "Special Characters" http://clojure.org/api/cheatsheet
I say that not being particularly accomplished with it. 15 years of programming Java, now 3 in Clojure, would dearly miss slurp and barf, the lesser appreciated virtues of homoiconicity.
http://www.draketo.de/english/wisp#text-table-of-contents
[ed: For Racket, see also: https://github.com/takikawa/sweet-racket ]
[ed2: See also the "readable" package for Common Lisp and for Scheme: https://sourceforge.net/p/readable/wiki/Install-howto/ ]
Your macros will break all over the place in languages that don't have this property.
http://srfi.schemers.org/srfi-110/srfi-110.html#wisp
^1 [ed: well, not "just python syntax", but if you wanted that, you would be better off using python. But python-like syntax/skin is absolutely doable. ]
See also: camlp4/camlp5.
And then there's the idea of pattern-match rewriting systems. iirc, GHC has something like that to rewrite certain expressions as optimizations, like:
(map f) . (map g) => map (f . g)
In a pure language, the two expressions are equivalent, but the latter is more efficient because it traverses the list only once rather than twice.The Cat programming language had a similar sort of "macro" system, but its web site seems to have disappeared. Shame, it was a really neat little concatenative language.
Homoiconicity is certainly neat, but it's not about macros at all. It's about having a Lisp-style `read` function. That's it.
Also, Dylan's syntax wasn't based on M-expressions per se, at least not in the "classical" LISP 1.5 sense of the term. It was just an Algolesque syntax rather than a façade layered above S-expressions. If you can claim that Dylan was based on M-expressions, then you could just as truthfully claim that Lua, Ruby, JavaScript, etc. are based on M-expressions. I won't say that it's strictly false, but I don't think that's quite how most people familiar with M-expressions think of them.
I know, my remark was more into the sense of being based as departure point, not as being a pure M-expression implementation.