Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

If it's true that you rarely need macros then isn't the awkwardness of s-expr syntax a very high price to pay just to have access to them?


I'd say it's more true that you rarely need to write a macro than it is that you rarely need to use them. Lots of very useful things across the language, from builtins like the threading macros (->, ->>) to libaries like Compojure, are built on macros.


I've found s-expressions to be one of my favorite things about Clojure. I understand that many people dislike them, and that many people get used to them, but I loved the syntax from the get go.


Sure. The syntax is distracting at first, but in my experience after a few weeks I saw past it. It's a non-issue for me at this point and I dearly miss it when writing Scala on another project.


What aspect do you dearly miss? (Honest question, I've never understood the S-expr trade-off, especially if macros are off the table).


Probably paredit. It's really so much better than non-structured editing. That said, there are structured editing plugins/modes/editors for other languages too.

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.


I thought that at first too about infixes being better (I've only been using Clojure for a few months), but I'm beginning to find the opposite. The only times when I miss an infix syntax is when doing math

    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.


I believe the parens are off by one in the Clojure example and that it should be:

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


I have to disagree.

    f x . g
reads just so much easier than

    (comp (partial f x) g)


I took some time to think about this because it started off as a feeling and not a list of reasons.

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


Structural editing, paredit, 100%.

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.


I have a pet theory that someone will put a Python skin on Clojure and have significant success with it.


Well, there's already wisp/sfri-110 for scheme, it appears the preprocessor should mostly work with clojure -- although one would probably want to port it over, or at least try to get it working on the jvm in order to have it "work where clojure works" (I have no idea how viable it would be to get it running with jython and something like kawa scheme out of the box):

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/ ]


You can't. The reason you can use macros so easily in LISP is because of homoiconicity: https://en.wikipedia.org/wiki/Homoiconicity

Your macros will break all over the place in languages that don't have this property.


SRFI-110 says you can^1: "Sweet-expressions are general and homoiconic, and thus can be easily used with other constructs such as quasiquoting and macros. In short, if a capability can be accessed using s-expressions, then they can be accessed using sweet-expressions. Unlike Python, the notation is exactly the same in a REPL and a file, so people can switch between a REPL and files without issues. Fundamentally, sweet-expressions define a few additional abbreviations for s-expressions, in much the same way that 'x is an abbreviation for (quote x)."

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


Thanks, I learned something today. :)


Dylan also has macros.


As does Rust.


But Rust isn't a Lisp based on M-Expressions like Dylan is.


No, but it's also not a homoiconic language, and it supports syntactic macros just fine. Homoiconicity might make procedural macros convenient, but it's far from a requirement. And then pattern-template macros are just as convenient in a more "syntactically rich" language as they are in a homoiconic one.

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.


> Also, Dylan's syntax wasn't based on M-expressions per se,

I know, my remark was more into the sense of being based as departure point, not as being a pure M-expression implementation.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: