I personally think Arc is a fantastic way to learn Lisp:
#1 Arc is minimal. This makes it a lot easier to learn, because it has so few axioms.
#2 Arc heavily emphasizes recursion, and recursion is fundamental to computing in general. So it's important to understand recursion.
#4 Arc's macros are simple enough that it's easy to fully understand how they work.
Arc is basically a stripped down minimal Scheme with Common Lisp unhygienic macros. It shouldn't be hard to convert the Scheme code in SICP into Arc.
As to whether Arc would be a good first programming language in general... I don't know. I suspect that if you take two programmers, one learns a functional language first, and the other learns an imperative language first, that they would have radically different mindsets.
Because of the recent surge of parallelism, functional languages have a significant advantage, because immutability and purity give both increased correctness and performance compared to mutability. So I don't think it's a bad idea to learn a more functional language like Arc first.
I tend to learn best by creating things, and seeing as I already know HTML and CSS, a language that would allow me to dive right in by creating web apps would be great.
There's also the fact that if someone asked me why I chose Arc over any other Lisp, I wouldn't really know what to say. I like the sound of your philosophy, but at the moment I don't really have the knowledge to understand how Arc is different from other Lisps (other than by being very minimal).
Maybe once I've got some practical experience and a good grasp of the basic concepts behind Lisp and macros, I might start learning Arc, and be able to appreciate it for what it is.
You're right, Clojure is not a bad choice either, in my opinion. It's even more heavily functional than Arc, so it's an excellent way to ease into functional programming. There are certain things I really dislike about Clojure, but on the other hand there are some things I absolutely love about it. I think it gets a lot of things right.
I think Arc is a better language for learning Lisp, but you are correct that Clojure would be better for writing actual applications that do things. Solving actual problems with actual applications can give you a lot of motivation, and motivation is important when learning anything.
The reason I use arc: I can't seem to stop. Before arc I tried to learn common lisp and scheme, but they required me to be persistent, and I kept falling off the wagon. With arc, I didn't need to be persistent. That was the concrete, tangible benefit of minimalism for me: the entire experience seemed to cater to me. There were fewer things that were like, "this may seem overly complex, but there's good reason for it, you'll understand when you gain experience." That argument is almost always a rationalization for accidental complexity (https://en.wikipedia.org/wiki/Accidental_complexity), in my experience.
Eventually I grew more fluent with common lisp and racket as well. You're right that they're more grown-up, and sometimes one needs something grown-up and industrial-strength. But arc was the gateway drug, and I can't seem to outgrow it.
I said this in another thread: "What really got me into arc was writing programs in one split window with the friggin compiler for my language in the other." (http://arclanguage.org/item?id=18954) That's really valuable, because to get really good at programming requires breaking out of every box other programmers create for you. Yes it's more immediately valuable to learn to use a few platforms or libraries. But understanding how they work, the design decisions and tradeoffs that are common to all platforms, that gives you superpowers in the long term.
In fairness, all my experiences predate clojure, which may well have all the same benefits. Several people have switched to it from arc in the past. I'd love to hear about your experiences after you try it out. I haven't much experience with it, though I've heard that the boxes it creates are harder to break out of (https://plus.google.com/110981030061712822816/posts/KaSKeg4v...)
I'm curious what docs you looked at that seemed to require familiarity with lisp. Feedback most appreciated.
These docs (https://arclanguage.github.io/ref/) are the ones I was talking about. I haven't read much, but there's no mention of parentheses anywhere. Also, there are symbols in circles to the left of concepts, e.g. F ! ? M, but no explanation of what they mean.
Generally, the docs seem rather terse to me; there's a lot of stuff that only makes sense now, after reading (http://aphyr.com/posts/301). "Clojure from the ground up" does start slowly, but by the end of Chapter 4, I really understood the idea of recursion and representing code as a tree, something that I don't think I could have got from the Arc docs.
One big difference is that Arc is very minimal, while Common Lisp has lots of libraries and a large standard library. In Arc, you'll end up writing a lot of stuff yourself. But because Arc tends to be more concise than Common Lisp, this can be fun rather than frustrating.
In large part because Arc is so minimal, it only really has two compound data structures: cons cells and mutable key/value dictionaries.
With a bit of effort, it's possible to use Racket libraries and Racket data structures. It's easier to do this in Arc/Nu and Anarki, compared to Arc 3.1.
Arc also does not have a module system, so name collisions are a very real possibility. If your code base is small this isn't a problem, but it does make it a bit trickier to use libraries written by other people.
Another difference is that Arc takes after Scheme, so it has tail-call optimization and full first-class continuations. Recursion is quite common.
Like Scheme, Arc has a single namespace for both functions and variables, unlike Common Lisp which has separate namespaces.
Arc intentionally makes no promises of backwards-compatibility: new versions of Arc might be radically different and completely break your code. Common Lisp, on the other hand, tends to be very stable, and cares a lot about backwards-compat.
Essentially, you can think of Arc as being very similar to Scheme, but much more minimal and concise, and with Common Lisp style unhygienic macros.
"Immutable-js Map Transient" means an Immutable-js Map that uses "withMutations" for extra speed.
I also want to test out ClojureScript's data structures, but I haven't gotten around to it yet.
In addition, notice that according to those benchmark numbers, you can create 20 empty AVL trees, then insert 100 keys into each AVL tree (for a total of 2,000 insert operations)... once per millisecond. In this case, worrying about the performance cost of immutability is a huge premature optimization.
(I'm not directing this at anybody in particular, in fact I am the kind of person that does premature optimization, so these numbers help with my own personal fears about performance.)
I'll test the performance of unsorted arrays soon and post the results here.
How disappointing. Mori (which uses ClojureScript's data structures) was either the same as Immutable-js, or significantly worse. In the end, AVL trees win by a large margin for small to medium dictionaries, while Immutable-js performs better for large (> 100 keys) dictionaries.
Get/insert/remove 1 key:
Immutable AVL Tree x 37,909,434 ops/sec ±0.37% (101 runs sampled)
Immutable-js Map x 19,492,874 ops/sec ±0.15% (101 runs sampled)
Mori Hash Map x 2,306,565 ops/sec ±0.74% (96 runs sampled)
Mori Sorted Map x 13,424,409 ops/sec ±0.51% (97 runs sampled)
Immutable AVL Tree x 6,257,569 ops/sec ±0.43% (99 runs sampled)
Immutable-js Map x 2,111,085 ops/sec ±1.07% (91 runs sampled)
Mori Hash Map x 1,553,193 ops/sec ±0.77% (93 runs sampled)
Mori Sorted Map x 3,785,671 ops/sec ±0.43% (96 runs sampled)
Immutable AVL Tree x 3,426,260 ops/sec ±1.38% (97 runs sampled)
Immutable-js Map x 1,415,893 ops/sec ±0.41% (96 runs sampled)
Mori Hash Map x 699,113 ops/sec ±0.40% (98 runs sampled)
Mori Sorted Map x 1,550,116 ops/sec ±1.54% (100 runs sampled)
Get/insert/remove 10 keys:
Immutable AVL Tree x 21,954,005 ops/sec ±0.81% (98 runs sampled)
Immutable-js Map x 17,236,706 ops/sec ±1.02% (99 runs sampled)
Mori Hash Map x 2,474,120 ops/sec ±0.77% (95 runs sampled)
Mori Sorted Map x 911,264 ops/sec ±0.41% (100 runs sampled)
Immutable AVL Tree x 399,700 ops/sec ±0.15% (97 runs sampled)
Immutable-js Map x 218,274 ops/sec ±0.63% (98 runs sampled)
Mori Hash Map x 150,978 ops/sec ±0.74% (96 runs sampled)
Mori Sorted Map x 73,598 ops/sec ±0.68% (98 runs sampled)
Immutable AVL Tree x 135,120 ops/sec ±0.76% (99 runs sampled)
Immutable-js Map x 100,893 ops/sec ±0.20% (97 runs sampled)
Mori Hash Map x 74,750 ops/sec ±10.95% (96 runs sampled)
Mori Sorted Map x 42,696 ops/sec ±0.45% (99 runs sampled)
Get/insert/remove 100 keys:
Immutable AVL Tree x 6,467,149 ops/sec ±0.38% (93 runs sampled)
Immutable-js Map x 14,233,214 ops/sec ±1.05% (96 runs sampled)
Mori Hash Map x 2,513,928 ops/sec ±1.24% (98 runs sampled)
Mori Sorted Map x 384,132 ops/sec ±0.53% (98 runs sampled)
Immutable AVL Tree x 19,760 ops/sec ±0.52% (100 runs sampled)
Immutable-js Map x 12,798 ops/sec ±0.26% (100 runs sampled)
Mori Hash Map x 10,619 ops/sec ±2.59% (93 runs sampled)
Mori Sorted Map x 3,078 ops/sec ±1.49% (98 runs sampled)
Immutable AVL Tree x 6,420 ops/sec ±0.51% (101 runs sampled)
Immutable-js Map x 6,204 ops/sec ±0.20% (99 runs sampled)
Mori Hash Map x 5,394 ops/sec ±0.81% (93 runs sampled)
Mori Sorted Map x 1,757 ops/sec ±0.51% (100 runs sampled)
So, today I learned that cons cells, despite having O(n) behavior, are really fast. They outperform mutable JS arrays at random inserts!
It's only once you get up to ~100 elements that AVL trees start to outperform cons cells. A good optimization would be to use cons cells for small lists, and then automatically switch to AVL trees once the list grows to be a certain size.
It includes algorithms for getting/inserting/removing an element at a particular index (in the case of arrays), and code for getting/inserting/removing an element in sorted order (for dictionaries and sets).
* : Adding in sorted arrays shouldn't be hard, but I haven't had any need for them yet.
It seems the problem with Anarki is that Arc's lists are terminated with the symbol 'nil rather than Racket's null. So you have to convert from Arc lists to Racket lists (and back again). Here is the relevant code: