I wonder if the author thinks the traditional if statement is flawed in the same way.
Most of my if blocks _are_ a single s-expression for each branch. I guess it depends whether you prefer to write functionally or imperatively. pg stated somewhere that, if the choice is forced, he will make the functional version of the code shorter, rather than the imperative version of code.
I also find it very interesting that the author doesn't indent the code at all. I hope it's just to make the point in this article, and now how ey normally writes code.
I also find it very interesting that the author doesn't indent the code at all.
I have a feeling (hope) that that was unintentional---like the blog formatting is just eating whitespace or something. Also, the first "Arc if" has a missing right parenthesis and uses = for comparison. :)
I was hoping other people would investigate their code! :) My personal arc directory filtered by hand for completeness (I have a lot of half-finished projects lying around), plus some stock Arc stuff I forgot in the last post:
You're right about blocks, so let's just focus on single-line 'if's. I can't think of a single non-lisp that allows adjacent condition and action without an intervening token. C and Java require parens, Go and Perl require curlies, Python requires the colon, Ruby requires a newline or semi-colon.
if (condition) action; # C, Java
if condition { action; } # Go, Perl
if condition: action # Python
if condition; action; end # Ruby
The 'else' token is a further separator. Never in these languages will you ever have two either-or expressions side by side. They're separated by either 'else' or 'else if' or 'elif'. Perl and Ruby even sometimes use 'if' as a separator.
action if condition # Perl, Ruby
Am I missing any counter-examples? I think the biggest error in this article is to blame Arc, when it's just extending the logic all lisps have always had. If you permit such adjacencies as (if condition action) and (cond ((condition action))), then Arc's choices don't seem so unreasonable.
(Though Clojure's mixture of Arc 'if' and optional types is a whole new level of unholiness.)
"Though Clojure's mixture of Arc 'if' and optional types is a whole new level of unholiness."
Is this what you meant instead? "Though Clojure's 'let', which mixes Arc's 'withs' with type hints, is a whole new level of unholiness."
Even so, I think Clojure's 'let doesn't actually have any[1] added complexity when it comes to type hints. It might look like the list is bunched into groups of either two or three depending on whether a type hint is present...
(let [^String x "x string"
y 2]
(body-goes-here))
...but that's not a quality of 'let. That's a quality of the ^ syntax. An occurrence of ^ consumes the next two s-expressions, just like ' consumes the next one s-expression:
So the bindings of a 'let are consistently bunched into groups of two s-expressions, just like Arc's 'withs.
[1] Of course, the type hints are actually used for optimization at some point, so the complexity of parsing them has to go somewhere. This is exactly as complex as destructuring: Arc's 'withs syntax supports destructuring, but it doesn't need special-case destructuring logic because it just translates down to 'fn. As it happens, Clojure's 'let syntax also supports destructuring, and it probably uses the same general technique.
Yes! Turns out I didn't notice the switch from if to let.
I have to say, though, I have no sympathy for the argument that it's still groups of two s-expressions. As a reader it's still more onerous to have to mentally group:
^a b
compared to:
'a
So the presence or absence of parsing complexity feels irrelevant.
I can't think of a single non-lisp that allows adjacent condition and action without an intervening token.
I mean, isn't this requirement mostly because those languages are infix anyway? Parsing gets easier with explicit ways of separating things. I could easily imagine shift/reduce conflicts or what-have-you cropping up when you try to eliminate the requirement for, say, parentheses around conditionals in Java.
For example, in some hypothetical infix language that doesn't require conditional separators (parens, braces, then, etc.), would
if x==10-x ...
be
if (x == 10) -x ... // maybe this language doesn't use the "return" keyword,
// so the conditional is returning negative ten.
or
if (x == (10 - x)) ...
?
Because Lisps use s-expressions, "intervening tokens" (per se) are unnecessary. As you say, Arc's choices don't seem so unreasonable, considering that.
Yeah, that's a good point. Non-lisps use keywords and punctuation for readability and to make parsing tractable, and the two reasons are often hard to separate in any single design decision.
To summarize my position: I have some sympathy for the specific argument that multi-branch 'if's are harder to read in lisp than in traditional languages[1]. But this affects any arrangement of parens, whether traditional 'cond' or arc 'if'.