I had been under the impression that it was supposed to last for one hundred years, not languish for one hundred years, but I suppose I might have misread something :/
I don't know what exactly agile developers would care about, but the guiding principle of Arc is to make programs short. Partly because that's what programming languages are for, but also because programs are meant to be changed, and shorter programs are easier to change. The latter part sounds like it should be relevant to them.
This is one reason why it is important for you to be here: some simple things like the problem with 'splice are clearly bugs, but other things are not. For example, the fact that '(1 . 2 . 3) reads as '(2 1 3) is just something drained accidentaly from mzscheme's reader or is part of Arc? Since you've written Arc, you're the only one to really know this.
We can only suppose.
I get this feeling that Arc is the language that is just going on by momentum.
Further, I don't think Arc, PG version, is as well-designed as you think.
For example: "code is spec". Apparently the code itself is to be the specification for the language. However, this brings up some questions: for example, does the fact that (ssplit '(1 2 3) 0) return ((1) (2 3)) part of the specification, or is that a bug? http://www.arclanguage.org/item?id=8450 .
Or how about the cute little fact that the PG 'map function can be safely recontinued when you capture the continuation in the mapping function if the given structure is a list, but is not safely recontinuable if the given structure is a string? Is this a deliberate design decision, an oversight, or something you don't specify and so any other implementation can do what they want?
And while tagging allows the programmer to arbitrarily create new types, those new types don't get along well with the builtin functions. I can't build a vector type in PG Arc that will work with the builtin functions. I can't build a quaternion type which I can multiply with a scalar number using '* .
These are design points which have been belabored for years; Brooks in 1975 attacked using "implementation as spec", pointing out that it limits future implementations by forcing them to always use the old implementation, for example.
Arc is, arguably, simpler than either Scheme or Common Lisp. The only reason for learning these other languages first, in my opinion, would be because of the greater availability of teaching materials.
In fact, if there's any language I'd recommend learning before arc it would be Haskell. You'll enjoy arc much more if you have a good understanding of functional programming first. The best way to do that (although a pretty hardcore way) is to learn Haskell.
As you will see from the link, the compiler is still far from being complete, but is has macros and it can compile itself. I know that this project is quite overlapping with arc2c (it generates assembly instead of C), but I started the project from scratch to learn more about compilers and not to build a full-blown Arc system. This is why the name is "Not Yet an Arc Compiler" -- I don't know if it will ever be Arc compatible, but I hope one day it will be :)
I briefly tried porting Arc to the new PLT Scheme earlier, and ran into problems. I have some suggestions at http://arclanguage.org/item?id=7057
As for lojic's question about why not just use PLT Scheme? That's a very good question. Personally, I find Arc has a lot of negatives compared to PLT Scheme, and the only positive I see is the macro system isn't confusing like Scheme's. Arc also provides the excitement of exploring new territory, but I think I've about exhausted that.
What about the rest of you? Why use Arc instead of Scheme? (Maybe this should be a top-level question?)
MzScheme has mzc, which does bytecode compilation. You can create something like standalone executables by embedding a copy of MzScheme into the module you're compiling. See http://download.plt-scheme.org/doc/372/html/mzc/ for more details.
(module test.arc "arc-exe-init.scm"
(require "ac.scm")
(namespace-require "ac.scm")
(require "brackets.scm")
(use-bracket-readtable)
(aload "arc.arc")
(aload "libs.arc")
(aload "test.arc") ; or the output of (acompile "test.arc") could work
)
Then
$ mzc --exe test test.arc.scm
mzc version 360, Copyright (c) 2004-2006 PLT Scheme Inc.
[output to "test"]
$ ./test
Hello, world
The results you get hardly seem worth the effort. With this, you'd still need to distribute with some form of ac.scm et al.
$ mv test ~/Desktop/
$ cd ~/Desktop/
$ ./test
default-load-handler: cannot open input file: "~/Desktop/ac.scm" (No such file or directory; errno=2)
=== context ===
#f::352: loop
There are probably smarter ways of compiling that don't have this hang-up, but in the end you'll still have kind of a large binary with MzScheme embedded.
If you just want the Scheme equivalent of Arc code, Arc already compiles down to Scheme. To get this compiled version (even if you can't run it directly without loading ac.scm, brackets.scm, arc.arc, and libs.arc):
$ cat test.arc
(prn "Hello, world")
$ mzscheme -m -f as.scm
Use (quit) to quit, (tl) to return here after an interrupt.
arc> :a
> (acompile "test.arc")
Hello, world
#t
> ^D
$ cat test.arc.scm
(ar-funcall1 _prn "Hello, world")
There are two classes of people: people who want to pay you, and people who don't.
Consider focusing all your attention on people who want to pay you. How can you make it really easy for them to pay? What do they want, and how can you do more of that for them? How do you find more people who want to pay you, how do you let them know you exist? Is your app so great that the people who want to pay you are telling their friends who want to pay you?
People who don't want to pay you don't make any difference whether they're using your service or not (you're not getting any money from them either way), unless there are so many of them that it's costing you something in server load or your time, in which case you can do something about it based in the specific pattern of abuse that you're getting.
It think that trying to force people who don't want to pay you to pay you is probably wasted effort, they'll just go away and not pay someone else :-)
hmm, this bit me... can't use each within an afn because each uses afn internally, and it leaks the 'self binding into the body. I changed it to use an rfn with a uniq name and it works.
The first thing that comes to my mind is more of a philosophy rather than a feature. One of the Agile principles is don't implement things until you need them... no speculative development: the tenth principle down in http://agilemanifesto.org/principles.html reads "Simplicity--the art of maximizing the amount
of work not done--is essential." The XP process implements this with its rule that you only write code to implement the current iteration, no writing code to implement features that you "know" will be coming in future iterations.
When I first read the Arc web server code (srv.arc) I was astonished at how simple the code was.
srv.arc doesn't do everything I want, but other web frameworks don't do everything I want either. And I've found it much easier to hack srv.arc to do what I want, precisely because it isn't massively bloated with "framework" stuff, stuff that we imagine we'd need to write a web application, but we don't actually need or it turns out we need in some other way when we actually write an application.
Showing off how easy srv.arc is to hack is a great example of the Agile simplicity principle. Need to get a servlet to output a custom header? Plow through the documentation to find the right addHeader() invocation and what object to call it on. Need to get Arc to output a custom header? (prn "My-header: foo")
Perl, Python, and Ruby are successively closer approximations of Common Lisp.
I admire the ideas behind Clojure because it makes a different set of design decisions from Common Lisp. This seems to have even gotten the attention of Common Lisp developers, and those guys are pretty hard to impress.
The focus on immutability, literals for data structures other than lists that share a common "seq" interface, first class functions and closures, multi-methods without OO, lazy sequences, and full fledged macros hit an interesting sweet spot that is not touched by any other language I know. Mr. Hickey has managed to put all of those things together in a way that strengthen, reinforce, and complement one another. That is an impressive feat of language design.
My point is that Clojure is not "just" popular. By making unique decisions about the semantics of the language, and not just improving the syntax over the semantics of an existing language, Clojure may very well be one of those languages whose ideas remain influential long after it stops being popular.
(I'm curious, does Arc innovate in terms of its semantics being significantly different than its predecessors? It seems to have the goal of taking existing Lisp paradigms and
making it possible to express them more succinctly. But I might be missing something important.)
So, even by the criteria you put forth, Clojure might be a language worth watching.
Perl had the popularity Arc could have had. Then Python did, then Ruby.
I will point out that Perl still has the popularity. Python and, particularly, Ruby have dramatically fewer developers and lines of code than Perl. But, then, Perl has fewer developers and lines of code than PHP. So, unless PHP is the exception that proves the rule, the popularity argument is obviously moot. But, I do think there's something to be said for pragmatism. While Perl (or Python, Ruby, etc.) has its flaws, it also has a vast community of people working on solving them...will Perl 6 be ready before arc is usable for a similarly wide array of problems? Seems pretty likely.
This is just one guy's opinion, and I'm hardly a 133t Lisp hax0r. But I'm not a part of this community to ride the next big wave. I'm here because I'm tired of the next big waves, because they almost always suck. I'm here because I want to be able to latch on to something that actually provides me with some small measure of joy, and that has a chance of having some real, lasting impact.
> But there's a bigger question: ever since I started writing web apps, I've heard the mantra "keep no state on the server". Arc's continuation or closure thing looks like it's totally breaking the rules. "What about scalability!!", as we say in javaland. So someone's got it all wrong, and why doesn't Hacker News fall over more often?
As far as I know Hacker News is only one server. Presumably it's pretty well tuned, and besides, there may be lots of hackers there, but I'm sure the readership is much less than, say, friendster, which I'm sure has to solve larger scalability problems.
This may also very well be the reason why Yahoo rewrote Viaweb ^^
> the server needs to store 30 separate closures
Closures are cheap ^^. For example in arc2c, a closure has one cell (cell=4 bytes for 32bit, 8 bytes for 64bit) for type, one cell for length, one cell for function, and one additional cell for each captured variable. Often the captured variables are just one or two, so on a 64-bit system you're looking at 40 bytes for each closure.
On the other hand a string might take up just as much space, and would result in not-so-clear code.
Well, since I voted for the basic library with sin, cos, pi, gcd, etc, I felt obligated to implement a basic math library with sin, cos, pi, gcd, closely related fucntions, and everything I needed or thought I'd need to implement those.
Implementations guaranteed not to be optimally efficient:
Edit: Fixed a mistake in bringing out-of-period numbers into period in sin and cos
(= pi 3.141592653589793238)
(= 2*pi (* 2 pi))
(def signum (n)
(if (> n 0)
1
(< n 0)
-1
0))
(def floor (x)
(if (< x 0)
(- (trunc x) 1)
(trunc x)))
(def ceil (x)
(if (< x 0)
(trunc x)
(+ (trunc x) 1)))
(defmemo fac (n)
(if (is n 0)
1
(* n (fac (- n 1)))))
;Existing mod only accepts integers. Left intact as "modulo"
;This mod copies the behavior of Ruby's mod
(def mod (dividend divisor)
"Returns the remainder from dividing dividend by divisor.
Adds divisor once more if they are of different signs so that the result is always of the same sign as divisor."
(if (is divisor 0)
(error "modulo undefined for 0")
(isnt (signum dividend) (signum divisor))
(+ divisor (- dividend (* divisor (trunc (/ dividend divisor)))))
(- dividend (* divisor (trunc (/ dividend divisor))))))
(def sin (x)
"Returns the sine of x in radians."
(let x (let red (mod x 2*pi)
(if (> (abs red) pi)
(- red (* (signum red) 2*pi))
red))
;Taylor polynomial; 0.0 is to cast to float
(- (+ 0.0 x (/ (expt x 5) (fac 5)) (/ (expt x 9) (fac 9)))
(/ (expt x 3) (fac 3)) (/ (expt x 7) (fac 7)))))
(def cos (x)
"Returns the cosine of x in radians."
(let x (let red (mod x 2*pi)
(if (> (abs red) pi)
(- red (* (signum red) 2*pi))
red))
;Taylor polynomial
(- (+ 1.0 (/ (expt x 4) (fac 4)) (/ (expt x 8) (fac 8)))
(/ (expt x 2) (fac 2)) (/ (expt x 6) (fac 6)))))
(def tan (x)
"Returns the tangent of x in radians."
;Lazy definition
(/ (sin x) (cos x)))
(def int? (x)
"Returns whether x is an integer"
(is (mod x 1.0) 0.0))
(defmemo prime (n)
"Returns the nth prime. 2 is the 0th prime."
(if (< n 0)
nil
(is n 0)
2
(let prev-primes (map prime (range 0 (- n 1)))
((afn (i)
(if (no (breakable:each p prev-primes ;Each always returns nil, so a break returns t
(if (int? (/ i p))
(break t))))
i
(self (+ i 1))))
(+ 1 (last prev-primes))))))
(def prime-factorization (n)
"Returns a list each prime up to the greatest prime in n paired with the power of that prime in n.
E.g.: (prime-factorization 20) returns ((2 2) (3 0) (5 1)).
Use (reduce * (map [apply expt _] ...)) to change a prime factorization into the number."
(rev:accum keep
(let p-ord 0
(while (> n 1)
(with (p (prime p-ord)
pow 0)
(until (isnt (mod n p) 0)
(++ pow)
(zap [/ _ p] n))
(keep (list p pow)))
(++ p-ord)))))
(def gcd (x y)
"Returns the greatest common divisor of x and y."
(reduce * (map [apply expt _]
(map (fn (a b)
(list (car a) (min (cadr a) (cadr b))))
(prime-factorization x) (prime-factorization y)))))
(def lcm (x y)
"Returns the least common multiple of x and y."
(/ (* x y) (gcd x y)))