Arc Forumnew | comments | leaders | submitlogin
3 points by rocketnia 2967 days ago | link | parent

Well, Clojure has unhygienic macros too.

I think Clojure fits most of the criteria that would lead someone to choose Arc. I think Clojure's main flaw compared to Arc is that it's a bit cumbersome to do iteration, because there's no general support for tail call optimization.

Arc has a few things positively going for it:

* Arc's implementation doesn't implement the whole language from scratch. Instead, syntaxes, data representations, and primitive operations are inherited from Racket, and most of the high-level tools are implemented in Arc itself as a library. What remains in the Arc implementation is a small, unintimidating core focused on some compilation and pattern-matching features. Since the core is small, it's easy to make certain modifications if needed. (Modifications to things inherited from Racket, like changing the reader syntax, are more challenging.)

* It extends s-expression syntax in a few minor ways. I think one of these extensions, the (a:b:c d) shortcut for (a (b (c d))), is particularly compelling. It tends to reduce lines of code, indentation, and parentheses all at once:

  (blah-blah-foo
    (blah-blah-bar
      blah-blah-baz))
  -->
  (blah-blah-foo:blah-blah-bar
    blah-blah-baz)
* Paul Graham wrote influential essays about language design that led to the release of Arc. Some people, including me, came to Arc because they read those essays and liked the high-level goals and priorities they expressed. Arc probably isn't even the best existing manifestation of those goals, but it is a Schelling point at least.


2 points by akkartik 2967 days ago | link

> Well, Clojure has unhygienic macros too.

Huh, I didn't know that about Clojure!

Yeah, I agree with everything you wrote. It was an unprecedented experience to hack on programs with the compiler for them open in a split window.

-----

2 points by rocketnia 2967 days ago | link

I'm drifting off topic, but imagine this: When you call (eval ...), imagine you pass in the global namespace that the code will run in. (Maybe we're using aw's extension for this purpose.) When you pass in a namespace that contains your own implementation of (eval ...) itself, you've effectively modified the compiler, but only as far as that specific code is concerned! As long as our custom compilers are written in Arc, we can treat them like we treat Arc libraries, and we can mix code that uses different compilers. We can have all kinds of compilers open in split windows at the same time. :-p

We already have plenty of examples of first-class namespaces, like aw's implementation posted recently. So all this would take is an implementation of Arc in Arc. Do we have one of those? I thought I heard of one at some point.

My excitement is not because I think a pileup of various compilers in a single codebase is a great idea, but because I think it's easier to share code this way. Compiler hacks are prone to merge conflicts that inhibit code sharing, but sharing libraries is... well, not perfect in Arc either, but it's at least ameliorable by doing some simple search-and-replace renaming or by agreeing on a namespacing framework (like my framework in Lathe).

This came to mind because I was recently realizing that in my language Staccato, my Staccato self-compiler was approximating a style of programming much like that split window of yours, without sacrificing modularity.

-----

2 points by akkartik 2967 days ago | link

By an odd coincidence, somebody just pointed me at Expansion-passing style today: http://lambda-the-ultimate.org/node/4588

-----

2 points by zck 2966 days ago | link

Clojure also has a pretty cool way to not have to call (uniq) by hand. If, inside a backquote, you append a # to a symbol, clojure will replace that variable with a gensym. And it'll use the same gensym every time you use that variable in the backquoted form.

Here's the source for `and` (https://github.com/clojure/clojure/blob/clojure-1.7.0/src/cl...):

    (defmacro and
      "Evaluates exprs one at a time, from left to right. If a form
      returns logical false (nil or false), and returns that value and
      doesn't evaluate any of the other expressions, otherwise it returns
      the value of the last expr. (and) returns true."
      {:added "1.0"}
      ([] true)
      ([x] x)
      ([x & next]
       `(let [and# ~x]
          (if and# (and ~@next) and#))))
See how it uses and#, but it doesn't capture the variable and?

I'm not entirely sure how you would capture a variable (e.g., Arc's "aand"); if you try to, clojure errors by default. There's probably some way, but I don't know offhand.

-----

3 points by rocketnia 2964 days ago | link

This StackOverflow answer ends with "I'd recommend a real anaphoric macro" and gives an example: http://stackoverflow.com/questions/9764377/how-to-avoid-anap...

Based on that, I found this way to write aand, which seems to work at http://www.tutorialspoint.com/execute_clojure_online.php:

  (defmacro aand
    ([] true)
    ([x] x)
    ([x & next]
     `(let [~'it ~x]
        (if ~'it (aand ~@next) ~'it))))
  
  (prn (aand "woo" (list it it) (list it it it)))
It looks like Clojure macros can capture variables in the other direction too, like Arc:

  (defmacro capture-it
    ([] 'it))
  
  (prn (let [it "woo"] (capture-it)))

-----