> Embedding docstrings in with the source also imply that functions and macros should be documented as they are written
This is how it should always be done. Encoding your ideas in any programming language is a lossy operation: it's difficult or impossible to determine from an arbitrary piece of source code what the intent and purpose of the programmer originally was. Good documentation gives the missing pieces.
I've found it incredibly helpful to write the documentation of functions before the function itself. Like you, I have a small head, and unless I know exactly what I should program beforehand, it's going to be difficult and messy to keep all details in mind. Describe the intent and purpose, give a canonical example of use (single line, or a couple of more if the function is variadic), rule out illegal input, and you're set.
Note that I'm not advocating long pieces of documentation. If you cannot describe a function well in a few lines, it's too big, and it's definitely not clear to you what it should be. If the documentation is in the same place as the source code, it is worlds easier to write the documentation, write the source code, then bounce between them while developing (since both usually require iteration).
I do not want to see idiotic documentation in Arc programs, but I doubt people who pick up Arc would do that anyway. I've seen many systems written in, e.g., Java, where documentation follows the same pattern as many Microsoft application help systems: "If you press the button called Print, you can print the document." or "Check here to enable grayscale printing" as a tooltip to a checkbox called "Enable grayscale printing".
"Embedding docstrings in with the source also imply that functions and macros should be documented as they are written"
Not at all! It's been a mind-blowing experience to me to see documentation pop up in all of the arc codebase just because we had a wikipedia-like workflow.
People often don't want to talk about what they're doing when they are doing it. It's just human nature. This suggests another great time to document code: not when you write it, but when you read it and figure out what it does. A wiki allows this.
Little discursive notes about why one thing works and something more obvious doesn't can be really helpful. Commentary about the code you didn't write can be helpful.
But in my experience on large projects that kind of documentation becomes:
# do_my_foo() is a function accepts a float and
# returns a float, performing necessary calculations.
double do_my_foo(double inarg) {
.
.
.
That is, they lie, they rot, and, since it's a "requirement" the programmer wasn't inclined to perform, they aren't informative, either.
Some questions to consider before a module system is implemented:
1. Why?
I can see two different use cases for modules: a) controlled sharing and reuse; b) one more tool to encapsulate functionality inside the program. Which one is it, or is it both? Neither?
2. Should modules (module names) map directly to the file system hierarchy?
Why should they? Aren't they a different concept? Would it be reasonable to have more than one module per file, or more than one file per module? And what would happen when I wanted to move files?
Why shouldn't they? Aren't they ultimately saved in files?
3. Do we really need multiple namespaces?
Only a single namespace? Consider this: let the file or module where execution started from be the namespace. Let each imported/loaded module have an anonymous, uniq'd namespace from which symbols are imported. Repeat recursively, and you only get conflicts if you explicitly import two same symbols in a module.
1. Reuse. Encapsulation is largely unnecessary, given with and withs (although I think we need withrec).
2. no:no:no:no:no:no:no
3. What you described is still multiple namespaces, even though we are just "working" on a single one - and besides, who says we'll be working on a single namespace when the Arc Server can serve several applications?
I was under the impression cadaver was talking about infix math, because looking in the second position for the functional argument allows infixy syntax analysis.
You could combine it with the dot operator of course but I wouldn't really see a reason for it. (Why would you use 3.+.4 instead of (3 + 4)?)
And actually, the example you gave doesn't quite work (because of quote, you can't actually put a symbol in the functional position using the . operator).
If by that you mean counting the nodes, you need to parse the source first. You can either do it by hand (if you know the grammar) or tweak the compiler/interpreter, if it doesn't have any statistics option.
Is the prefix notation the biggest reason Lisp is not used more? No, I don't think so. Prefix, infix, or postfix is all a matter of what you are used to. It's possible to define the max operator as infix instead of prefix:
(max 1 2) ; --> 2
1 max 2 ; --> 2
It's associative (it's easy to prove). And after that you can do the regular stuff:
1 max 3 max 2 max 5 ; --> 5
Does this look natural? Usually max is more of a function, or in mathematics one usually writes
max{ <some set> }
or with set comprehension
max{ x : <some condition for x> } .
I find infix max easier in pen-and-paper calculations.
Coming back to Lisp and Arc, is familiarity the only reason you want infix math operators? The biggest benefit you get from prefix compared to infix is its regularity and uniformity. Control structures, function calls, and (now in Arc) array and hash table access are all identical. This means you can freely add not only new functions, but new control structures, without hacking the parser and the compiler.
One reason why this happens is that you don't have precedence levels. Precedence is always explicit (bar Arc's new intrasymbol operators, which are infix), and this is why Lisp mathematical functions, such as + and *, accept multiple arguments. The distinction between (+ 1 (+ 2 3)) and (+ (+ 1 2) 3) is useless and even harmful. It's best to write it (+ 1 2 3) or 1 + 2 + 3, because the operation is associative.
Aside from losing this, you also lose the concept that your whole program is a list. This stands at the heart of Lisp macros. How would you write macros for opaque infix blocks?
> Aside from losing this, you also lose the concept that your whole program is a list. This stands at the heart of Lisp macros. How would you write macros for opaque infix blocks?
The solution which dwheeler on the readable-discuss list supports is to simply have the reader translate infix to postfix. Syntax like {1 + 2 + 3} becomes readily translated into (+ 1 2 3). What's more, dwheeler suggests that precedence should not be supported at all: {1 + 2 * 3} is a badly formed expression that the reader will reject, you must use {1 + {2 * 3}}.
That's an interesting idea, but on the other hand, if you don't have precedence defined for the common cases, how are infix expressions better than prefix? Does it save typing and cognitive load?
{1 + {2 * 3}}
(+ 1 (* 2 3))
Maybe it's a bit clearer, as it's more familiar. Could this be extended to arbitrary two-parameter functions?
(def foo (x y) ...)
{x foo y} ; --> (foo x y)
Why do I ask? Because then we would have a way to write
; "Object-oriented" method call
{object -> method}
; Different kinds of assignments, common in C/C++/Java/etc.
; (Perhaps of little worth in Arc.)
{x += 1}
{x -= 1}
{x |= bits}
; A "binding" operator, or Pascal-like assignment.
{x := 1}
; An arbitrary relation.
{a R b}
; Perl 6 pipe operator [1] look-alike.
{{data ==> [...]} ==> processed}
> Maybe it's a bit clearer, as it's more familiar. Could this be extended to arbitrary two-parameter functions?
This is, in fact, the reason why dwheeler decided to eschew precedence - I kept pestering him that in the future I might want to create a 'convoke operator or some other arbitrarily-named relation. Basically any {_ sym _ [sym _]...} pattern gets converted to (sym _ _ ...)
The problem with supporting precedence is that you have to define it before someone starts mixing up the order of the symbols. And dwheeler wanted to do it at read time, so you had to have fixed precedence at read time. If someone randomly typed {x sym1 y sym2 z}, the reader wouldn't be able to know if it's (sym1 x (sym2 y z)) or (sym2 (sym1 x y) z). Initially we were thinking of supporting only +-*/ precedence and disallow mixing of other operators, but this started to get slippery: where do you draw the line where some operators are allowed to have precedence and others are not?
This suggestion may be a bit un-Arc, but I'd prefer a "with-infix" macro implementing a well-defined sub-language than I would some kind of integration into the reader. An optional precedence list arg might be a good idea.
The problem there is, of course, that the s-exprs generated by the macro are essentially a black box, but in my thinking, if infix is being used minimally and purely for convenience's sake, it isn't necessary to make those s-exprs accessible to other macros (right?...).
$s[i] # A scalar at index i
@s[<something complex>] # An array slice (i.e. multiple values)
$#s # The last index of the array s
a .. b # In list context, a list of numbers or letters from a to b, inclusive (can be used outside array indexing)
Actually this is not critical code, so the relative immaturity of Arc is not really an issue. It's just a frontend for people scared by rsync or scp and it probably won't be used more than once a month anyway.
Spending too much time on it : that would have been the bad option. And using Perl or Python or PHP would have taken me much longer.