We just have a formal definition for + that we don't have right now. (As a side note, this is close to the actual implementation of the '+ defined in ac.scm). How can we know it works for strings, lists ? Does it works on chars or not ?
The goal is not to get rid of, say, -, * and / and implement them in Arc. Their signification is obvious and no one would need to see how they are implemented. This is not a math class. But this is not true of +, because by opening the black box it is, we have :
- the possibility for alternative implementations of the arc compiler, as most of the language is defined in itself. Imagine I want to work on a Forth implementation to put Arc code into fridges or TVs ; or pg thinks it's time to move on a C implementation to have a super-fast Arc. The smaller these axioms, the easier it is to get compatible compilers or to avoid bugs from one version to another.
- the possibility to say "I now want to use + for hash tables, let's add it, how is that implemented ?",
- the possibility to say "I don't need + to work for strings or chars or anything, I just want numeric addition because I'm crunching numbers ; +int is the function in need".
Incidentally, if I want to write +int, the only way to do so currently is to do (def +int a b (- a (- b))), and we both agree to say that it's a bad idea.
I just read back http://www.paulgraham.com/ilc03.html and found intersting things dealing with what we are talking about (pg doesn't answer, so I'll make hime talk myself ;)
"Letting people rewrite your language is a good idea. You, as the language designer, can't possibly anticipate all the things programmers are going to want to do with it. To the extent they can rewrite the language, you don't have to."
"If you want to overload existing operators to do the right thing when given your new type, you don't need anything new in the core. As long as you have lexical scope, you can just wrap a new definition of the operator around the old one."
" At the conference [pg gave this talk], John McCarthy pointed out that a function to invert a matrix might be better described by writing "inverts a matrix" than by giving the source."
I can't blame him. Cutting bloat in the language core is clearly a goal. Nothing should go into the official Arc release unless it has proven its value in real code (which is basically News.YC at the moment).
Sure, he has to keep the control over the things. The point is that a few bug fixes (the mkdir problem for example), simple conveniences (see arc.sh) and even trivial optimisations (arc< to name it) available in anarki are of interest even in the official release. I'm not talking about experimental stuff (infix numeric notation, vectors, standalone exe, maybe docstrings, experimental module systems, ...)
But I think the few fixes and conveniences should really be taken into consideration. I can't believe none of them are of interest.
They probably are of interest, but let's not forget he's running a company in his spare time, and 3 releases in about 3 weeks is a pretty good work rate.
Of course, as someone running Arc in Windows, I'd love it if a bit more stuff worked out of the box (e.g. the blog, which didn't work in Arc1 IIRC). That's why I'm probably going to switch to developing on Anarki and then testing it on vanilla Arc afterwards.
Note that I don't criticize Paul's attitude there. 3 releases in less than a month is much more than what I expected. He didn't release early, but at least he releases often :) I just meant a few things would deserve a little more consideration, at least in the next few weeks/months ?
A conditional such as if can only be implemented as a macro if it expands to another conditional such as cond, which must be implemented as a special form.
I think the logic is to make if a "primitive macro," the way + is a primitive function. Just as you can't define + in terms of Arc functions, you can't define if in terms of Arc macros. Nevertheless, (isa + 'fn), and so the thinking is that (isa if 'mac) makes sense.
There are valid reasons not to do things this way, of course. For instance, what's so powerful about macros is that they literally expand into a code tree, which (as you observed) if can't do. For that reason, why not have (isa if 'form) (or 'prim, or 'special, or something similarly descriptive)? Then you have first-class special forms without literally making them macros. This would be a big improvement over the current state of affairs:
arc> (type if)
Error: "reference to undefined identifier: _if"
.
If can be implemented as a primitive function instead of a special form given the fn operator. It could take three arguments: a condition, a then function, and an else function and then call the appropriate function. An example implementation is:
(def if2 (cond then else)
(if cond (then) (else)))
If2 would be given as a primitive function and its implementation would be hidden like that of cons is. Given access to if2, a basic if construct is:
(mac basic-if (cond then (o else))
`(if2 ,cond (fn () ,then) (fn () ,else)))
This would make code walkers easier because they wouldn't have to know about as many special forms.
I'm guessing this request was inspired by something similar to Ruby's method_missing method. method_missing is often used to implement behavior that involves the name of the message being passed to an object (the official example is a class that converts between Roman and Arabic numerals and uses method_missing to redirect messages such as "roman.III" to "roman.roman_to_arabic('III')"). Another usage is undefining all methods and then intercepting the calls, such as in RubyQuiz #95, where that approach was used to convert code blocks to S-expressions.
The former use isn't really that useful without an object system to restrict the scope of a single method_missing definition. And, of course, any object system could implement its own method_missing. And the second usage, in addition to being pathologically uncommon, could be implemented in Arc using macros, or, where function calls must be intercepted at runtime, by iterating through all values in the namespace (or even a closure as well) and overloading functions to add hooks that perform some other behavior if some boolean is set to true. (Of course, said boolean would probably need to be in a table keyed by the current thread.) Of course, that would require being able to retrieve a list of all values in a namespace or closure to iterate through (hmmm...now there's an idea).
I think PG meant that succintness and power are approximately the same. Ron seems to be taking PG's statement to mean power is defined as succintness, and then breaking that.
Just as atomic mass was just a proxy to the true period law for Mendeleev, I see succintness in terms of syntax tree nodes as a proxy for the true definition of power, which would probably be more like the amount of space a program takes to hold in one's mind. Given that, of course binding-block would reduce power, as it would require one to add meta-tags to the assignments in one's mental code tree.
A purity analyzer would need to be a little more complex than that - the "=" operator would be pure or impure depending on the scope of the location being set. The simple way to solve that would be to make locations created within any lexical context that is discarded with the completion of the function pure and those without impure, although they may be slightly complicated.
Of course, some situations, such as code that sets unset values in an outside hash table to the default value, may arguably be "pure" functional code, although dealing with such things is outside the domain of a "conservative" purity analyzer.
An alist is just a list whose structure conforms to a certain pattern. Overloading lists' function for when the argument is something other than numbers for alists makes as much sense as writing some very general tree code and giving its index function prefix tree behavior when called with strings.
Besides, that just wouldn't work if your alist is keyed with integers. Should (((1 . #\A) (2 . #\B) (3 . #\C)) 2) return #\B or (3 . #\C)?
I've thought about making alists work in functional position, but as Darmani points out, that would be ambiguous with integer keys. If you did allow it and gave preference to the alist interpretation, you'd then also have to be very loose about what you called an alist; you wouldn't want to check all the way to the end. So now you've got two levels of ambiguity, which starts to be alarming.
Certainly, it may be possible to give this functionality to lists tagged with the type 'alist. But then what would it do when I want to find the zeroth element of an alist where 0 is a key? You could have it just default to one or the other, but then I'd just end up ignoring whichever indexing operation it doesn't default to and using a differently-named function all the time for the other type of indexing in general alist code. In which case, what's the point of overloading the function in the first place?
No contest, my idea is half-baked. I had in mind symbols as keys, where you could define reasonable semantics. I'm still bugged by the lack of symmetry: For any X with which it makes some sense at all, (X ...) seems to work. Constants, hashes, strings, lists... even lambdas ;-)
Obviously the issue is that alists are only a convention.