I'm pretty sure he's always said Arc is a work in progress, and is not ready for full-fledged production use. I don't think he's ever recommended it to others.
I'm sure part of that is that he doesn't right now want to deal with people making requests for it. And having a userbase would mean that.
The creator (smihica, Shin Aoyama) hasn't come to the forum yet, but their work is impressive. Just look at that REPL start up in under a second! On my machine anyway. :)
Thanks to svetlyak40wt for discovering this project 80 days ago (http://arclanguage.org/item?id=18355). It was in active development then, and it's still active now.
---
The primary spark that led me to make Rainbow.js was a desire to respond constructively to threads like this one.
Using Rainbow was a side effect because I wanted to wow people with the execution speed, which conanite had already meticulously worked on in Rainbow. This was still tied into the desire to help those forum threads along: Between speed and portability, what possible excuse could there be not to use it? ^_^
Once I committed to particular technical goals involving consistency with Rainbow, the 20,000 lines of code were basically predetermined. It's kind of unfortunate that it would fail to be helpful in threads like this one, but it's not all a loss.
The secondary spark that led me to make Rainbow.js was that I wanted to get the hang of using JavaScript, after having worked mostly in Arc for a few years. That goal was met. :)
In Python (at least in Python 2.x, where map and filter return lists instead of generators), the equivalence looks like this:
[expr_involving_x for x in xs] == map(lambda x: expr_involving_x, xs)
[x for x in xs if expr_involving_x] == filter(lambda x: expr_involving_x, xs)
For example,
>>> [x + 1 for x in [1,2,3]] == map(lambda x: x + 1, [1,2,3])
True
>>> [x for x in [1,2,3] if x == 2] == filter(lambda x: x == 2, [1,2,3])
True
You can apply these transformations recursively until you remove the list comprehension notation altogether. E.g.,
[x + 1 for x in [1,2,3] if x == 2] -> map(lambda x: x + 1, [x for x in [1,2,3] if x == 2])
-> map(lambda x: x + 1, filter(lambda x: x == 2, [1,2,3]))
Nested comprehensions have a transformation rule, too, but it's somewhat more complicated:
[expr_involving_y for x in xs for y in expr_involving_x] == sum([[expr_involving_y for y in expr_involving_x] for x in xs], [])
For example,
>>> [y+1 for x in [1,2,3] for y in [x+1,x+2,x+3]] \
... == sum([[y+1 for y in [x+1,x+2,x+3]] for x in [1,2,3]], []) \
... == sum([map(lambda y: y+1, [x+1,x+2,x+3]) for x in [1,2,3]], []) \
... == sum(map(lambda x: map(lambda y: y+1, [x+1,x+2,x+3]), [1,2,3]), [])
True
So, list comprehensions are just a way of saying the same things as higher-order functions. Arc generally favors the higher-order function flavor of this idiom. map is still called map, filter is called keep, and Python's sum(..., []) is spelled (apply join ...):
You might think Arc's preference for these comes from Lisp dialects naturally eschewing specialized syntax. But, Arc does like syntactic shortcuts; just ones that apply more generally to functions, like bracket notation (e.g., [+ _ 1]) and ssyntax (e.g., ~f and f:g come up a lot in higher-order calls).
It's also interesting to note that due to the previous rules, you can easily write a macro that turns list comprehensions into the equivalent chain of higher-order function calls.
There are many Arc builtins that can help build lists, too. They're all defined in Arc itself, so just take a look through arc.arc to figure out how they work. Functions I spy that may help for list building (list of lists or otherwise): rem, mappend, trues, firstn, nthcdr, cut, tuples, pair, n-of, bestn, split, retrieve, dedup, reduce, rreduce, range, ... Really just depends on what you're looking to do!
4. You'll need to edit the data, then re-serve the page, in which case you use 'defop' (http://files.arcfn.com/doc/srv.html) or you can write the file out to your static directory.
[edit] #4: If you choose to use the static directory you may need to ensure arc is set up to serve out certain types of files (i.e. .js, etc). See http://arclanguage.org/item?id=10620. Anarki has already taken care of this, but arc proper has not.
Probably so that it can be changed more easily (by redefining 'make-br-fn rather than hacking the reader).
;arc.arc from Anarki
(mac make-br-fn (body) `(fn (_) ,body))
I recall at one point people talking about making brackets work so that, for example, [+ _x _y _z] = (fn (_x _y _z) (+ _x _y _z)). It would extract all arguments from the body beginning with _, alphabetize them, and insert them into the argument list. I don't know if Anarki actually did this, but I do believe it accepted [+ _1 _2 _3 ...] at one point.
"I still think that the bug in akkartik code is a result of too complicated one liner."
I'll make 2 objections to that:
a) That particular case was not a bug, but a performance issue.
b) The response to bugs isn't a more verbose formulation. Verbosity has its own costs to pay. Patterns that you could see in a single screen can no longer fit side by side, which can cause their own bugs.
If one-liners are to be avoided, you could just replace the call to reduce in your example with an explicit loop. But that's a bad idea, right?
Perhaps you're finding right-to-left hard to read. Stick with it; you'll find that it becomes easier to read with practice. Many of us started out with similar limitations. It's like learning to ride a bicycle; I can't explain why it was hard before and isn't anymore, but vast numbers of people had the same experience and you will very probably have it too. As you read more code you'll be able to read dense one-liners more easily. There is indeed a bound on how dense a line should be, but this example is nowhere near it.
The first problem is that it is a one liner and some times they hide nasty bugs
As a corollary to "sometimes code hides nasty bugs". ;)
One-liners aren't intrinsically bug-prone. I'd even argue that they're often less buggy, just because there's less code to get wrong. Akkartik's problem is actually an example: the issue was data structure choice, and the fixed code was still one line.
The second is that it is in a reverse order
Depends on who you ask. Nested function calls read fine to me, but people have built entire languages to avoid them (e.g., http://factorcode.org/).
If you look at the Arc compiler around line 448 in ac.scm from Arc 3.1, you can see that the first thing it does it is check whether the "fn" in the first position of a "call" form is a macro:
that's why an identifier being a macro takes precedence over an identifier being a lexical variable.
But you can easily change it. The "lex?" function will tell you whether an identifier is a lexical variable at that point. So you can change the test in the cond for "macfn" to something like "(and macfn (not (and (symbol? fn) (lex? fn env))))" and an identifier being a lexical will take precedence over an identifier being a macro.
arc> (when nil (prn "I will not get evaluated, because this is a macro."))
nil
arc> (def when2 (test . body) (if test (do body)))
#<procedure: when2>
arc> (when2 nil (prn "I get evaluated anyways, because this is a function call."))
I get evaluated anyways, because this is a function call.
nil
in is an interesting case, because it's a macro for efficiency reasons: it expands into simpler code with fewer function calls than, say, a call to some. This is kind of how Arc does function inlining, rather than making a compiler infer it. See http://en.wikipedia.org/wiki/Inline_function.
2. Different strokes for different folks, I guess. I don't think I've ever used the f argument of pair, myself. You can accomplish your goal with
Rtm is Robert Morris, who plays a role in Arc's implementation / design. This
would indicate that Paul Graham (the "main" designer of Arc) prefers to use
join, but is unsure which is best.
4. Ah, but then you'd lose information. e.g., the definition of mem is
(def mem (test seq)
(let f (testify test)
(reclist [if (f:car _) _] seq)))
Here, we want to return the entire sublist, not just the car, but we want to apply f specifically to the car of the sublist. So, we don't want to make reclist only work on the car, since we'd lose said sublist. That is, the current mem behavior is
arc> (mem 5 '(8 6 7 5 3 0 9))
(5 3 0 9)
But by adding car to reclist, we'd only be able to do something like
arc> (mem 5 '(8 6 7 5 3 0 9))
5
5. Tail calls should be recognizable in the call-graph of the code. For example, the mutually recursive functions
(def even/r (n)
(if (is n 0)
t
(odd/r (- n 1))))
(def odd/r (n)
(if (is n 0)
nil
(even/r (- n 1))))
involve tail recursion, even though neither even/r nor odd/r call themselves directly. Since the function in loop is a tail call (the ,gfn call is the last function before a return), it should still be optimized. Someone please correct me if I'm wrong.
6. No significance aside from naming conventions: asterisks follow the names of global variables.
(1) This one I think is the right decision. The real constructor is, after all, cons; and in fact, (is (type:cons 'a 'b) 'cons) but (isnt:alist:cons 'a 'b), since cons is more generic. Being a list is a property that certain sets of cons cells have, but it isn't their type.
(2) Again, I think this is the right decision. The point of obj is to be convenient in the simple case where you're building an object-like hash table: one with fixed fields and changing contents. I feel that table should probably take arguments like obj but without quoting (so that (obj a 1 b 2) would be (table 'a 1 'b 2)), but obj is still handy to have around.
(3) Ah, car/cdr versus first/rest. There's not really anything I have to add to this one, other than that I like the conciseness and composability. I've also seen fst and rst proposed, so that you can do frst for first:rest; make of that what you will.
(4) Yes, this is annoying. The rationale is the same as in the obj case, but I very often want to compare to non-symbols.
(5) If we accept that association lists are really just lists which have a certain structure, then we can't use special syntax, etc., as that already means something for lists. What if you have (= a '((1 a) (2 b))); would a.1 be a (since it's an association list) or b (since that's element 1 of the list)? I would also argue that the point of association lists is to be lists with special structure; if you want something like that that's its own type, that's what tables are for.
(6) I haven't really used deftem, but it does define a template; you create them using inst. On the other hand, obj creates the table right there.
I should say, by the way, that I don't think Arc is a perfect language by any means. Its bizarre naming conventions come to mind, for instance. But I happen to think many of these are non-issues (though I would be perfectly open to being convinced otherwise).
1. Brevity is top priority. If you learn all the arc functions there are lots of clever commands that anticipate common programming idioms and allow you to write very pithy code.
2. Encourages 50:50 mix of functional and imperative code for what each does best- See the accum command for an example. (If you implement this in clojure, be sure to use the new transients feature http://clojure.org/transients)
3. The design of the web server is elegant (though still somewhat alpha)
4. Call/cc is available (this is the one thing you won't be able to implement in clojure, unless you're some kind of super-guru :-)
5. Intrasymbol syntax is a really promising idea that still needs to be fleshed out a bit.
6. Simplicity- The code behind arc (i.e. the files ac.scm and arc.arc) are, by design, is extremely simple (much simpler than clojure)
IMHO, the value of arc to someone not interested specifically in language design is lower now that Clojure is available. Clojure took many good ideas from arc and expanded on them in a way that really cut into arc's value proposition.
I still think pg's rule of "brevity, brevity, brevity" is the right approach in the long run- Hopefully someone will find time to take the best of these two Lisp dialects and create a new language in the future that rethinks "brevity" in terms of the ideas behind Clojure. (I'm think it'll take more than just a library of macros and functions to do this)
It seems that plt4's immutable conses have been worked around via low-level pointer hacking. I can only hope this doesn't introduce subtle bugs, but given that Eli Barzilay seems to work on plt scheme, hopefully this is not the case.
I've pushed the changes to anarki's "official" branch.
I'm guessing v372 for Ubuntu probably works on Debian, since they're largely similar. If it doesn't, I've been running v360 on squeeze and haven't noticed any issues with arc3. Failing either of these, you could build v372 yourself.
Once MzScheme is installed, there's not much to do about Arc that's not already on http://www.arclanguage.org/install. You might consider aliasing it in your .bashrc (or whatever), and I recommend using rlwrap. e.g.,
alias arc='cd ~/arc3; rlwrap mzscheme -m -f as.scm'
That's sweet. One thing I'm appreciating about Clojure is the way Rich bent over backwards to make almost any traversable data accessible as a "sequence", effectively creating a single function library for dealing with lists, XML data, regular expressions, etc.
Since the name "Arc" is derived (according to a PG essay) from "arch", and since both start with the letter "A", why not modify an "A" to make it look a bit more like an arch? Simple and evocative.
Currently, ensure-dir and date use system to run Unix commands. It would be much more portable if they used mzscheme operations instead. This has caused me trouble not only on Windows, but different versions of Linux. And I think make-temporary-file could replace /dev/urandom.
This is long overdue, but I've finally posted an explanation of the implementation of my hygienic macro system for arc (http://arclanguage.org/item?id=8599). It's written from a general lisper's perspective, because the principles involved aren't arc-specific. It doesn't yet cover the modifications to the internals of ac.scm.