Arc Forumnew | comments | leaders | submit | Pauan's commentslogin
2 points by Pauan 7 hours ago | link | parent | on: DrRacket(Libs) and Arc

I just fixed a bug in Arc/Nu, so now you can do this:

  (%:require racket/class)
  (%:require racket/gui/base)

  (= frame (%:new frame% (label "Example")))
Notice that you can use () rather than [], because in Racket the syntax reader converts [] into ().

You need to require "racket/class" because Arc/Nu only requires "racket/base", rather than all of "racket".

reply

3 points by Pauan 2 days ago | link | parent | on: DrRacket(Libs) and Arc

I would just like to point out that Arc/Nu is fully compatible with Arc 3.1, and it doesn't need conversions between Racket and Arc, so it's the easiest way to deal with Racket in Arc programs.

reply

2 points by Pauan 32 days ago | link | parent | on: New logo

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.

#3 You learn about closures and first-class functions, which are very useful and are used in lots of other languages (including JavaScript, Python, and Ruby).

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

-----

2 points by jb 31 days ago | link

Re #2 and #3: I think Clojure also puts a heavy emphasis on closures, first-class functions, and recursion (although it seems you have to jump through a hoop to get TCO working (Java and JavaScript don't support TCO (yet))).

Arc may be more minimal and have simpler macros, but I think it'll take me a lot less time to actually start making things with Clojure because there are more resources available for learning it, and it has good support for making web apps (good libraries, good compiler to JavaScript). The Arc docs seem to be good, but targeted more at people who are already familiar with Lisp.

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.

-----

3 points by Pauan 31 days ago | link

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.

-----

2 points by akkartik 31 days ago | link

Sounds like you have a decision :)

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.

-----

2 points by jb 30 days ago | link

Hmm, some interesting reads.

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.

-----

1 point by akkartik 30 days ago | link

That's fair. I think we worked on the reference because the tutorial seemed pretty good. Do you think we need something in between?

Edit: I thought the tutorial had come up in this thread, but it hasn't. I'm not used to having two questions from newcomers at once :) Are you aware of http://old.ycombinator.com/arc/tut.txt?

-----

2 points by jb 27 days ago | link

Ahh, I got a server error when I first tried to look at that; it seems to be working now, though. I've read the tutorial - it does seem pretty good. Thanks for the link :)

-----

1 point by akkartik 27 days ago | link

Yeah, sorry about that. I think the HN guys made some changes and broke us for like a day.

-----


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.

-----

3 points by Pauan 38 days ago | link | parent | on: Re: waterhouse's AVL trees

Here are some performance metrics, using Node.js v0.10.22 and http://benchmarkjs.com/. Higher numbers are better.

Get from a dictionary with 1 key:

  Mutable object     x 27,279,012 ops/sec ±0.74% (97 runs sampled)
  Frozen object      x 26,178,537 ops/sec ±0.18% (101 runs sampled)
  RB Tree            x 43,176,557 ops/sec ±0.34% (94 runs sampled)
  Immutable AVL Tree x 38,223,676 ops/sec ±0.12% (101 runs sampled)
  Immutable-js Map   x 19,359,187 ops/sec ±0.22% (100 runs sampled)
Insert 1 key into an empty dictionary:

  Mutable object             x 6,957,772 ops/sec ±1.96% (96 runs sampled)
  Mutable object copying     x 6,323,751 ops/sec ±0.62% (99 runs sampled)
  Frozen object copying      x   201,268 ops/sec ±1.56% (91 runs sampled)
  RB Tree                    x 7,872,867 ops/sec ±0.42% (100 runs sampled)
  Immutable AVL Tree         x 6,349,293 ops/sec ±0.63% (97 runs sampled)
  Immutable-js Map           x 1,872,937 ops/sec ±0.17% (102 runs sampled)
  Immutable-js Map Transient x 2,056,430 ops/sec ±1.75% (96 runs sampled)
Insert 1 key into an empty dictionary and then remove 1 key:

  Mutable object             x 2,217,063 ops/sec ±0.72% (99 runs sampled)
  Mutable object copying     x 1,993,320 ops/sec ±0.58% (98 runs sampled)
  Frozen object copying      x   143,611 ops/sec ±1.48% (93 runs sampled)
  RB Tree                    x 3,923,739 ops/sec ±0.41% (95 runs sampled)
  Immutable AVL Tree         x 3,511,233 ops/sec ±0.38% (97 runs sampled)
  Immutable-js Map           x 1,334,255 ops/sec ±0.55% (100 runs sampled)
  Immutable-js Map Transient x   886,683 ops/sec ±0.16% (103 runs sampled)
----

Get from a dictionary with 10 keys:

  Mutable object     x 28,446,194 ops/sec ±0.44% (99 runs sampled)
  Frozen object      x 25,922,457 ops/sec ±0.24% (99 runs sampled)
  RB Tree            x 16,603,460 ops/sec ±0.43% (97 runs sampled)
  Immutable AVL Tree x 22,367,775 ops/sec ±0.61% (95 runs sampled)
  Immutable-js Map   x 17,869,208 ops/sec ±0.28% (100 runs sampled)
Insert 10 keys into an empty dictionary:

  Mutable object             x 600,844 ops/sec ±0.71% (100 runs sampled)
  Mutable object copying     x 137,961 ops/sec ±0.16% (102 runs sampled)
  Frozen object copying      x   8,982 ops/sec ±1.77% (97 runs sampled)
  RB Tree                    x 914,296 ops/sec ±0.25% (99 runs sampled)
  Immutable AVL Tree         x 414,025 ops/sec ±0.48% (100 runs sampled)
  Immutable-js Map           x 219,537 ops/sec ±0.44% (101 runs sampled)
  Immutable-js Map Transient x 305,737 ops/sec ±1.06% (98 runs sampled)
Insert 10 keys into an empty dictionary and then remove 10 keys:

  Mutable object             x 302,855 ops/sec ±0.74% (100 runs sampled)
  Mutable object copying     x  49,878 ops/sec ±0.57% (98 runs sampled)
  Frozen object copying      x   8,899 ops/sec ±1.59% (95 runs sampled)
  RB Tree                    x 534,523 ops/sec ±0.54% (99 runs sampled)
  Immutable AVL Tree         x 144,249 ops/sec ±0.16% (99 runs sampled)
  Immutable-js Map           x 111,517 ops/sec ±0.37% (99 runs sampled)
  Immutable-js Map Transient x 173,799 ops/sec ±0.48% (100 runs sampled)
----

Get from a dictionary with 100 keys:

  Mutable object     x 25,410,034 ops/sec ±0.19% (102 runs sampled)
  Frozen object      x 25,156,950 ops/sec ±0.27% (101 runs sampled)
  RB Tree            x 23,728,410 ops/sec ±0.17% (100 runs sampled)
  Immutable AVL Tree x 21,287,235 ops/sec ±0.40% (97 runs sampled)
  Immutable-js Map   x 14,605,600 ops/sec ±0.55% (92 runs sampled)
Insert 100 keys into an empty dictionary:

  Mutable object             x 58,322 ops/sec ±0.55% (100 runs sampled)
  Mutable object copying     x    818 ops/sec ±0.44% (99 runs sampled)
  Frozen object copying      x    139 ops/sec ±0.93% (81 runs sampled)
  RB Tree                    x 51,910 ops/sec ±0.45% (99 runs sampled)
  Immutable AVL Tree         x 20,738 ops/sec ±0.47% (100 runs sampled)
  Immutable-js Map           x 14,000 ops/sec ±1.65% (97 runs sampled)
  Immutable-js Map Transient x 37,714 ops/sec ±0.16% (102 runs sampled)
Insert 100 keys into an empty dictionary and then remove 100 keys:

  Mutable object             x 31,602 ops/sec ±0.56% (100 runs sampled)
  Mutable object copying     x    729 ops/sec ±1.54% (97 runs sampled)
  Frozen object copying      x    137 ops/sec ±2.12% (80 runs sampled)
  RB Tree                    x 31,957 ops/sec ±0.43% (99 runs sampled)
  Immutable AVL Tree         x  7,122 ops/sec ±0.47% (99 runs sampled)
  Immutable-js Map           x  6,001 ops/sec ±0.57% (99 runs sampled)
  Immutable-js Map Transient x 19,958 ops/sec ±0.49% (99 runs sampled)
----

"Mutable object" means a plain old mutable JavaScript object.

"Frozen object" means a plain old mutable JavaScript object that was made immutable using `Object.freeze(foo)`.

"Mutable object copying" means a plain old mutable JavaScript object that was first copied before performing each insertion/deletion.

"RB Tree" means mutable Red Black trees that I implemented in JavaScript: https://github.com/onilabs/stratifiedjs/blob/91d173989c64181...

"Immutable AVL Tree" means waterhouse's algorithm, ported to JavaScript: https://github.com/onilabs/stratifiedjs/blob/c1e5d670784f659...

"Immutable-js Map" means https://github.com/facebook/immutable-js.

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

----

As I said earlier, AVL trees are fast. They tend to be 0-3x slower than JavaScript objects.

Stop and think about that for a moment: JavaScript objects are mutable, they're written in C++, they're heavily optimized with various tricks, and they're probably implemented as hash tables.

Meanwhile, AVL trees are immutable, do not use any kind of optimization tricks (the algorithms are very simple and straightforward), and they are implemented in vanilla JavaScript, in less than 400 lines of code.

JavaScript engines are fast. So fast that immutable AVL trees are completely viable. I would not hesitate to replace all my programs with them. This is quite amazing.

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.

-----

2 points by Pauan 38 days ago | link

How disappointing. Mori[1] (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)
---

* [1]: https://github.com/swannodette/mori

-----

2 points by Pauan 38 days ago | link

As promised, here are the benchmarks for unsorted arrays: http://pastebin.com/raw.php?i=DyTHMHNZ

You can also see it in chart form: http://i.imgur.com/BI0NDoD.png

----

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.

-----

2 points by Pauan 38 days ago | link | parent | on: Re: waterhouse's AVL trees

In case anybody wants it, here is the JavaScript code for sorted dictionaries, sorted sets, and unsorted arrays[1]:

https://github.com/onilabs/stratifiedjs/blob/c1e5d670784f659...

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

If anybody wants, I can translate the code from JavaScript to Arc.

---

* [1]: Adding in sorted arrays shouldn't be hard, but I haven't had any need for them yet.

-----

2 points by Pauan 38 days ago | link | parent | on: Re: waterhouse's AVL trees

I used Arc/Nu (https://github.com/arclanguage/arc-nu).

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:

https://github.com/arclanguage/anarki/blob/0c4eb66c162f973e7...

-----

2 points by akkartik 38 days ago | link

Pushed, thanks. https://github.com/arclanguage/anarki/commit/af2ea9c8f4

-----

3 points by Pauan 41 days ago | link | parent | on: Re: waterhouse's AVL trees

In fact, the normal binary search tree deletion can sometimes cause an unbalanced tree when concatting two arrays, but waterhouse's "amerge" function works correctly!

-----

3 points by Pauan 565 days ago | link | parent | on: Does Arc Support Escape Sequences?

All of the string syntax that Arc currently uses:

http://docs.racket-lang.org/reference/reader.html?q=string%2...

Though this may change in the future, if pg switches away from Racket's reader. So I'd recommend not using any of the funky escapes.

-----


That's probably because pauan@arclanguage.org doesn't exist. My e-mail is pcxunlimited@gmail.com

-----

More