What is worth noting in the above post is the "With the PLT web server" part.
What I mean is most of the other posts demo how to do it with some required (exotic) framework. This example is just using the basic web server APIs of the PLT continuation based web server.
In other words in this example, Eli BOTH writes the framework (in the first 9 lines), then uses the framework in the last 4 lines.
PLT's mzscheme is in effect, a very distant cousin to Scheme. If they weren't all eggheads and had at least one attentive marketing person, they would have labeled it some new fangled SuperLispIncarnateNextGeneration (SLING) moniker and received a bit of buzz.
I haven't looked at exactly how PG implemented Arc over MzScheme, but consider this. Much of what he did _could_ be done using no more then PLT mzscheme's out of the box, macro system, custom language module capability and custom reader capability, by anyone. Yes, even you.
In some sense most of Arc is just what anyone does who develops in PLTs Scheme/laguage system, i.e., create their own custom language/DSL. PG's may have done it a bit better, more extensively, with a general purpose intent.
So what is the secret weapon here Arc, or the underlying system that allows someone to create an Arc so easily?
You could, right now, be using PLT MzScheme to do your own Arc(s), your own, just for you and your friends custom language/DSL.
Ruby, Python are cute, and deserve having a user base, but PLT mzscheme is without doubt, the best and most powerful language system out there that "no one talks about."
Let me tell you a story about a language called "Stutter". It's a full m-expr language. Function calls have the syntax
f[x], math quite sensibly uses a + b * c notation, etc. The only
weird thing is that assigning a variable uses the set special form -
set[x 42] - because of some weird stuff in the parser that is only
tangentially related to our discussion.
Now I'll introduce to you a special syntax in Stutter. In Stutter, ()
introduces a data constant called an array, which is just like any
other sequential data collection in any other language. So it's
possible to do something like this in Stutter:
set[var (1 2 3 4)]
Stutter is a dynamically-typed language, and arrays can contain
strings, numbers, or even arrays:
set[var (1 "hello" ("sub-array" 4 ))]
Let me introduce to you something new about Stutter. In Stutter,
variable names are themselves data types. Let's call them labels
(this is slightly related to why we have a set[] special form). And
like any other data type, they can be kept in arrays:
set[var (hello world)]
Remember that () introduces an array constant. So (hello world)
will be an array of two labels, hello and world. It won't suddenly
become (42 54) or anything else even if hello is 42 and world is 54.
The variable's name is a label, but the label is not the variable (at
least not in the array syntax; it was a bit of a bug in the original
implementation, but some guys went and made code that used labels in
that manner and it got stuck in the language spec).
The array is just like any other array in any other language. You can
concatenate them like so:
set[var append[(array 1) (array 2)]]
=> now var is (array 1 array 2)
You can add an element in front of it like so:
set[var cons[1 (some array)] ]
=> now var is (1 some array)
Array access syntax is not very nice, but it does exist:
nth[1 (this is the array)]
=> this returns the label "is"
You could create an empty array with:
nil[]
And you could create an array with a single element with:
array["hello"]
=> returns the array ("hello")
Oh, and remember those guys who abused the labels in array syntax I
told you about? Well, they created a sort-of Stutter interpreter, in
Stutter. However, they sucked at parsing, so instead of accepting
files or strings or stuff like that, their Stutter interpreter
accepted arrays. They were going to make the parser later, but they
just really sucked at parsing.
They called their Stutter interpreter "lave", because they were hippie
wannabes and were tripping at the time they were choosing the name.
It was supposed to be "love", but like I said, they were tripping.
Of course, since lave accepted arrays, it couldn't get at the nice
f[x] syntax. So they decided that the first element of an array would
be the function name as a label. f[x] would become the array (f x).
lave had some limitations. For example, instead of Stutter's nice
infix syntax a + b, lave needed (plus a b). Fortunately lave included
a plus[x y] function which was simply:
define plus[x y]
x + y
So how come these guys became so influential? You see, Stutter's BDFL
is a bit of a lazy guy. He is so lazy that he didn't even bother to
fix up the syntax for if-then-else. In fact, there was no
if-then-else. What was in fact present was a ridiculously ugly cond
syntax:
cond[
{ x == y
...your then code...
}
;yes, what can I say, Stutter's BDFL is lazy
{ !(x == y)
...your else code...
}
]
lave's creators pointed out that you could in fact represent the above
code, in lave-interpretable arrays, as:
(cond
( (eq x y)
...your then code...)
( (not (eq x y))
...your else code...))
Then they created a new Stutter function which would accept 3 arrays, like so:
if[
(eq x y)
(...your then code...)
(...your else code...)]
You could then use an if-then-else syntax like this:
lave[
if[ (eq x y)
(...your then code...)
(...your else code...)
]
]
Then they thought, hmm, maybe we can integrate this into our lave
function. So they wisely decided to create a new feature in lave,
called "orcam". I think it was supposed to be "okra", but
unfortunately I asked them about it while they were tripping, so maybe
I just got confused.
Basically, you could tell lave that certain Stutter functions would be
treated specially in their lave-syntax. These functions would have
the "orcam" property set in some private data of lave. Instead of
just running the function, lave would extract the array components,
pass them to the function, and then run whatever array that function
returned. So you could simply say:
lave_set_orcam_property[(if)]
lave[
(if (eq x y)
(...your then code...)
(...your else code...)
)
]
Because of this, people started creating all sorts of
orcam-property-functions. For example, there was only a while loop in
the language (lazy, lazy). Someone created an orcam-property-function
called for:
define for[s c u code]
append[ (begin) //begin{} is just a compound statement
cons[ s
append[(while)
cons[c
cons[ code array[u]]
]
]
]
]
So you could do:
for[(set i 0) (less i 42) (preincrement i)
(begin (print i))]
And it would look like:
(begin
(set i 0)
(while (less i 42)
(begin (print i))
(preincrement i)
)
)
So if you wanted something like a C for loop you could do:
lave_set_orcam_property[(for)]
lave[
(for (set i 0) (less i 42) (preincrement i)
(begin
(print i)
)
)
]
It was particularly difficult to create nice orcam-property-functions,
but it was easier than trying to get Stutter's BDFL to move.
Soon after lave's creators added orcam-property-functions, Stutter's
BDFL decided to do something about the language. He was always
bothered about the bug in Stutter array syntax where something like
(hello world) would return, well, the array (hello world), instead of
sensibly returning an array with the values of hello and world. So he
introduced the `, syntax. An array constant prefixed with "`" would
have a special meaning. It would not be completely a constant.
Instead, when it saw a "," Stutter would evaluate the expression
following the comma and insert that element into the array being
created. So `(,hello ,world) could now become (42 54), if hello was
42 and world was 54.
Some of the top ocam-property-function writers realized that Stutter's
new `, syntax would really, really help. For example, instead of the
kludgy, virtually unreadable if code, you could just write:
define for[s c u code]
`(begin
,s
(while ,c
,code
,u
)
)
However, you weren't limited to just the `, syntax. It was usually
the best choice, but if there was a lave-expression array you wanted
that couldn't exactly be given by "`,", you could still use the good
old append[] and cons[] functions. In fact, for really complex
lave-expression arrays, a combination of the new `, syntax and the old
append[] and cons[] functions would do quite well.
Because of this, creating orcam-property-functions became easier and
their power skyrocketed. Other languages which automatically
evaluated variable labels in their arrays couldn't imitate it (so what
was originally a bug - that (hello world) did not evaluate the
variables hello and world - became a feature). Worse, those other
languages' arrays sometimes couldn't themselves contain arrays, or
even have different types.
Their arrays just weren't powerful enough to hold code, so other
languages never managed to create a powerful orcam-property syntax.
Eventually, people were writing Stutter programs like so:
lave[
(define (fn x)
(if (less x 1)
1
(times x (fn (minus x 1)))
)
)
]
And so, Stutter's BDFL decided to be even more lazy and simply wrote Stutter as:
while[true]{
print[ lave[ read[] ] ]
}
so that everyone didn't have to keep writing "lave[]"
I don't feel any rush in producing new versions, just as I didn't feel any rush in releasing the first one. Lisp hackers have waited 50 years for a good implementation. That didn't kill Lisp. Hackers used to this idea aren't going to panic because Arc hasn't been updated for n months. And the ones who feel that an actively growing source tree is more important than the underlying language were in the wrong place to begin with.
The next release will have more improvements to news.arc than the underlying language, because that's what I've been working on most lately. But I'm going to be focusing more on the language soon.
This was just an early snapshot release, so for me at least, the canonical source is arcn.tar. I don't know much about the conventions for this sort of thing, but I'm still working on the foundations, and getting that right, especially, is something best done by very small (even n=1) groups. All the more so since CL suffered so badly from doing the opposite; the core of the language seems like it was designed by 20 different people, each with slightly different design philosophies, but all with slightly overlapping functional territories.
I plan to do releases frequently though, and I've been incorporating suggestions from this site.
Personally, I think the only thing that went wrong in the history of Arc is that pg didn't correct people's misunderstandings about what it was, and what it was trying to achieve. A 100-year language is not going to be usable tomorrow, and a language designed by the optimal number of people (one) is not going to accept many patches!
If people want a practical language to use now (and there's nothing wrong with that!) then they should go ahead and build one. I'm happy to join any project that anyone may have for developing an Arc-like language. I might even start one myself (why not? It'll be fun!) if I have the time.
A language doesn't survive in its implementations, but in its ideas. Modern languages (especially Python/Ruby, but even C# and Java) are becoming Lisp. Dynamic typing, late binding, functional programming, closures, generic functions, MOP... the 'popular press' laughed at them, but now these are the hot topics in language design. Those ad hoc, informally-specified, bug-ridden, slow implementations of half of Common Lisp are getting better every year. Common Lisp and Scheme will die, but in 10 years everyone will be using Lisp, and they won't even know it.
The same goes for Arc. Clojure already uses some ideas from Arc (e.g. fewer parens in conditionals). If the ideas are good (and I think they are) then they'll spread. If people want to build new languages (e.g. Arc3F) that use them, then that spreads them further, That, I think, is the true future of Arc, as a source of eternal ideas.
Oh honestly. Perl had the popularity Arc could have had. Then Python did, then Ruby. The fact that the "winner" keeps changing shows that it doesn't matter which one you beat.
I've long since stopped worrying about other languages. If they're genuinely better, they deserve to win. And if they merely have "momentum," they'll ultimately be superseded, just like every other language du jour before them.
Thanks for the highly accurate description of PLT and PLT Scheme. I founded PLT as an academic but I am one who appreciates the necessity to open a channel of communication between the 'real' world and academia.
While Matthew and Robby are the "drivers" (with lots of co-pilots :-) I think that setting this tone early in the project has placed PLT Scheme naturally in a lonely niche: it is a real scripting language with capabilities that rival those of everything out there and it is also a serious academic infrastructure. Typed Scheme -- the first and only sound 'gradual typing' language so far -- is just an example of what I mean. We can publish about this in the flagship research conference on programming languages and at the same time, we are using it for its intended applications. Sam Tobin-Hochstadt is porting a part of DrScheme to Typed Scheme as I type. It is this kind of experiment -- porting a piece that your "life" depends on -- that puts us squarely on the applied side, too.
1. The time scale. I don't want to make what people think they want right now. Following that recipe earlier would have got me Perl, which lost its lead to the language I would have gotten a little later, Python, which lost its lead to the language I would have gotten a little later, Ruby, which... See the pattern?
2. The audience. "Make something people want" is a recipe for companies. Their goal is to make a lot of money, which means aiming for a wide audience. That shouldn't necessarily be one's goal in every kind of work. It's ok to want to be Jane Austen instead of Perez Hilton, even though Perez Hilton is what most people want.
Not quite. Arc has various bits of (gasp!) syntax. There are 10 pieces of syntax: () [] ' ` , ,@ ~ : . and ! . A quick breakdown:
1. () you know--function application, macro application, or a list. Basic stuff.
2. [] creates an anonymous function of 1 (or, on the Anarki, n) arguments. [...] is transformed (more or less) into (fn (_) ...).
3. '... is the same as (quote ...); whatever is inside becomes a literal. 'a is a symbol, '(a) is a list....
4. `... is the same as (quasiquote ...); the same as (quote ...), but evaluated things can be inserted with (unquote ...) and (unquote-splicing ...).
5. ,... is the same as (unquote ...); as observed, `(.1. ,... .2.) is similar to (list '.1. ... '.2.).
6. ,@... is the same as (unquote-splicing ...); the same as unquote, but ... must be a list and is spliced in. `(.1. ,@... .2.) is similar to (join '(.1.) ... '(.2.)).
7. ~func is the same as (complement func), which returns a function which is conceptually similar to (fn __ (no (apply func __))), i.e. which returns the boolean opposite of whatever the original returned.
8. f1:f2 is the same as (compose f1 f2), which returns a function which is conceptually similar to (fn __ (f1 (apply f2 __))), i.e. which applies f1 to the return value of f2 applied to the arguments.
9. x.y is the same as (x y); it's designed for nice structure access, e.g. lst.3 instead of (lst 3).
10. x!y is the same as (x 'y); it's also designed for nice structure access, e.g. my-table!password instead of (my-table 'password).
There are a couple of things wrong with your macro. The first is that your (list ,@args) is inside ,(each ...) which is inside `(do ...); you can only have as many unquotes (,s or ,@s) as you have quasiquotes (`s), and you have two within one. The second is that "each" is only run for its side-effects. The return value of "each" is always nil. Thus, even if your macro worked, it would expand to (do nil), which isn't what you want. To replace "each", you want (map func lst), which returns a list where func has been applied to each element of lst, e.g. (map - '(1 2 3)) returns '(-1 -2 -3). In my macro, the function returns a list of the form (report-result ARGUMENT 'ARGUMENT); the `',_ construct means "quote the value of _," since ,_ is within a `. Splicing this (map ...) into the (do ...) block will give you what you want. Is that clear?
Also, a handy tip for debugging macros: (macex1 '(an-expression ...)) will expand the first macro call in (an-expression), which can help you see what's going wrong.
I can't tell you if this is the right place to ask these questions, but having some place for them would definitely be a good thing. I'm usually happy to answer them, though.
x!y!z is (x 'y 'z) rather than ((x 'y) 'z); likewise x.y.z. While perhaps not a bug per se, this is clearly undesirable (at some point, adding more infix dots is less clear than just parenthesising, so x!y!z for (x 'y 'z) is not very useful, but chaining lookups is common and is much nicer to read as x!y!z than ((x 'y) 'z)) and should be fairly simple to fix.
()() ()()
# # # #
__#_#_#_#__
{_` ` ` ` `_}
_{_._._._._._}_
{_ H A P P Y _}
_{_._._._._._._._}_
{_ B I R T H D A Y _}
.---{_._._._._._._._._._}---.
( `"""""""""""""""""""""` )
`~~~~~~~~~~~~~~~~~~~~~~~~~~~`
The first three are right. As for 4, it's not so much that I prefer less communication as that I sometimes may be too busy to visit the site.
I think the thing people may not understand is that I can only work on Arc intermittently. In the spring as well as a YC cycle we had startup school and I got married. The next day the summer YC cycle started. During the summer I was busy with a larger than usual number of startups. As soon as it was over I went on honeymoon, during which I barely touched a computer. I got back to the US just before applications for the winter cycle were due. I've been reading them all the past week, and I still have over 100 left. (I probably wouldn't even be writing this if I weren't desperate for ways to procrastinate.) Then we have to move YC and ourselves to the west coast, then do interviews. I'm hoping that I'll be free to work on Arc in mid or late November, but I don't want to promise anything.
So it's not so much that I don't want to be held to a schedule as that I couldn't do things to a schedule even if I wanted to.
I'm hoping that ultimately doing so many things will make them all turn out better. But it does mean I can't do any of them more than part time.
In answer to your specific questions: I don't think I can commit to an upper bound for new releases, because the factors that determine whether or not I can work on Arc aren't cyclic. Yes, I'll incorporate bug fixes, the next time I get to work on the language, which I hope will be late this year. At the moment the most valuable things people interested in Arc could do are (a) find bugs in the current implementation, (b) think about the core language and specifically what new operators, if they existed, would make existing Arc programs like news.arc significantly smaller in tokens, and (c) try using Arc to write different kinds of applications and report what happens.
If we're throwing out random nitpicks, then I would vote for "number?" as the test function name, with "number" or "num" being the coercing function. I think the SICP style of appending a question mark at the end of boolean functions makes for very readable code, and keeps with the Arc design goal of brevity. I do not like "anum" or "anumber", because I think "a____" should in general be reserved for anaphoric macros, for consistency's sake.
Let me first state that I'm happy that Arc's gotten this far and I wish it - and PG - all the best with its future: I hope it's a bright one.
That said, I must disagree with Paul's recent blog entry/essay and this challenge...
Brevity or compactness as a measure of a language's "greatness" is a red herring. There are two acceptable methods by which source code can be compact: syntactic sugar and factoring/libs. I say "acceptable", because I don't consider putting everything on 1 line or using function names like "f" acceptable. And while syntactic sugar can be good at times, it can also lead to write-only code (see: APL, Perl (imo), and many others).
There's absolutely nothing special with PG's example code and the challenge isn't much of a challenge at all. Analogy: everyone has cars and I "invent" a jet, and then challenged everyone else to a race from Boston to LA. I've picked a challenge to show off what I consider to be my best trait: speed. Why didn't I challenge everyone to get from my home to the local grocery store? Given the terrain, perhaps a mountain bike would have been the best tool for that job.
How about this challenge for Arc:
On a little-endian machine, read in the Quicktime .MOV header atom format (big-endian), parse, and dump it to the console in a human readable format.
The reason I say there's nothing special with the example code is that we all know there's a lot of code going on under the hood behind it - a lot of code that PG has already written. And it's a cop-out to state at the end of the challenge... "Code to import standard libraries doesn't count..."
The code still had to be written. It isn't magical just because it comes standard. It shouldn't make a difference if it's included with the language or downloaded from the internet. The question isn't how much code was written, but rather, how much code did I have to write?
Again compactness is a red herring.
Perhaps Arc will be my language of choice for implementing <insert task>. It would be far more informative to me to be told (by PG) what problem(s) Arc is being design to solve and then show me how my life as a programmer would be easier - using Arc - to accomplish those tasks.
Again, I wish PG all the best with Arc, and I look forward to using it. But currently, I very much agree with Ron Garret when he said, "...[Arc] seems to pretty much punt on all the hard problems of language design."
How are we supposed to look at Arc then? The problem doesn't lay only in the fact that no source has been written, but in the fact that who has full control over Arc doesn't discuss about the language with the community. Is Arc just a one-man work and arclanguage.org just a way to show the world this work from time to time?
It's ridiculous that to bring you back in the forum we needed such a harsh post.
The problem with comments is that no one ever changes them when the code changes even tho the comment is right there! don't you see it! change that,too!!!
Moving docstrings away from the code would make things worse, creating multiple copies would make it even worser.
What do you think about the history of Arc so far?
From my perspective, it appears to be a history of different expectations.
On the one hand, it seems that pg:
1. is taking a long term view of the language
2. is focusing on core language issues
3. doesn't want to be held to a schedule
4. prefers less communication with the community
whereas, it seems at least part of the community wants:
1. to see *signs of life*
2. useful libraries
3. to know the plan (or whether there is one)
4. more interaction from pg (communication, acceptance of bug fixes, etc.)
As with many misunderstandings, better communication might go along way in helping to set proper expectations.
For example, I have some questions for pg that might be shared by others in the community:
1) What is a reasonable upper bound for producing new releases of Arc - 6 months, 3 years, ... ?
2) Will you accept bug fixes for the language in the future? If so, approximately when?
3) Besides the profiler you mentioned, what contributions would you like from the community? Do you want any contributions from the community?
I think some of the disappointment that is felt by some in the community is due in part to the potential we see in the language. Yes, we know you're taking a long term view, but when we saw the potential to use Arc in place of current tools, we hoped we could use it soon. Obviously it's usable now, but that's a relative term.
That's pretty obsolete. When I wrote that I was in the middle of trying to write a new Arc whose source was as clean as formal semantics but was actual working code. That turned out not to work because the resulting language was so slow I didn't want to use it for anything, and without applications to drive me I stopped caring about the language. What eventually happened after various experiments was that Robert and I wrote a new implementation that was a compromise between cleanness and practicality.
So now instead of starting with cleanness and trying to achieve practicality, the plan is to always work on practical stuff (like "always have running code") but constantly push the source toward cleanness. I feel like a lot of the definitions in arc.arc for example are close to final form.