Arc Forumnew | comments | leaders | submitlogin
4 points by zck 3983 days ago | link | parent

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.



3 points by fallintothis 3983 days ago | link

Most of my if blocks _are_ a single s-expression for each branch.

I was going to say...

  (def if-with-do (expr)
    (and (caris expr 'if)
         (some [caris _ 'do] expr)))

  (def accum-expr (accumf testf exprs)
    (map [ontree (andf testf accumf) _] exprs))

  (def keep-expr (testf exprs)
    (accum a (accum-expr a testf exprs)))

  (def count-expr (testf exprs)
    (summing s (accum-expr s testf exprs)))

  (defs count-ifs-with-do (exprs) (count-expr if-with-do exprs)
        count-ifs         (exprs) (count-expr [caris _ 'if] exprs)
        ifs-with-do       (exprs) (keep-expr if-with-do exprs)
        ifs               (exprs) (keep-expr [caris _ 'if] exprs))

  arc> (each file '("arc.arc"
                    "strings.arc"
                    "pprint.arc"
                    "code.arc"
                    "html.arc"
                    "srv.arc"
                    "app.arc"
                    "prompt.arc")
         (prn "=== " file)
         (let exprs (readfile file)
           (prn (count-ifs-with-do exprs) "/" (count-ifs exprs))))
  === arc.arc
  11/120
  === strings.arc
  3/18
  === pprint.arc
  5/5
  === code.arc
  0/0
  === html.arc
  4/24
  === srv.arc
  4/21
  === app.arc
  9/53
  === prompt.arc
  2/6
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. :)

-----

3 points by zck 3982 days ago | link

Nice code. Some of my lying-around arc files:

unit-test.arc 2/12

All of my project euler solutions: 4/98

-----

2 points by fallintothis 3982 days ago | link

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:

  (each file (lines:tostring:system "find . -name \\*.arc")
    (prn "=== " file)
    (let exprs (errsafe:readfile file)
      (prn (count-ifs-with-do exprs) "/" (count-ifs exprs))))

  === ./transcribe/transcribe.arc
  1/1
  === ./qq/qq-test.arc
  1/3
  === ./qq/qq.arc
  0/10
  === ./macdebug/macdebug.arc
  7/17
  === ./bench/nbody.arc
  0/0
  === ./bench/pidigits.arc
  1/1
  === ./ansi.arc
  1/1
  === ./news.arc
  16/99
  === ./sscontract.arc
  0/0
  === ./blog.arc
  0/0
  === ./libs.arc
  0/0
  === ./profiler/profiler.arc
  0/2
  === ./vim/gen.arc
  0/11
  === ./hygienic.arc
  0/3
  === ./trace/trace.arc
  1/4
  === ./contract/test.arc
  0/1
  === ./contract/contract.arc
  0/9
  === ./contract/special-cases.arc
  0/2
  === ./contract/reconstruct.arc
  0/2
  === ./contract/defcase.arc
  0/1
  === ./contract/test/canonical.arc
  0/4
  === ./contract/test/ssyntax.arc
  0/4
  === ./contract/ssyntax.arc
  0/5
  === ./contract/util.arc
  0/2
  === ./quick-check/quick-check.arc
  1/8
  === ./arity/arity.arc
  0/8

-----

1 point by akkartik 3982 days ago | link

Nice trick, passing accum functions around!

-----

3 points by akkartik 3982 days ago | link

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

-----

3 points by rocketnia 3982 days ago | link

"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:

http://tryclj.com/

  > '[^String foo "foo"]
  [foo "foo"]
  > (count '[^String foo "foo"])
  2
  > '[^String]
  java.lang.RuntimeException: Unmatched delimiter: ]
  > '^String foo
  foo
  > (meta '^String foo)
  (:tag String)
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.

-----

2 points by akkartik 3981 days ago | link

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.

-----

2 points by fallintothis 3982 days ago | link

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.

-----

2 points by akkartik 3982 days ago | link

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

[1] I see I said so to you before, at the end of http://www.arclanguage.org/item?id=16838.

-----