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.
Both of these can be done; they are in fact both instances of the "exceptional cases" I talk about under "Hygienic quasiquotation". I should probably have provided examples, so I'll do so now.
In order to do (1), you have to break up the stuff that's getting mappended. It needs to be a list, but it needs to contain syntactic unquotations. I think an example will explain better than words here, so here's your macro written with my hygienic quasiquotes:
(mac bias args
(withs (ws (map car pair.args)
xs (map cadr pair.args)
us (map [uniq] ws))
#`(with ,(mappend (fn (u w) `(,u #,w)) us ws)
(let r (rand (+ ,@us))
(if ,@(mappend
(fn (u x) (list `(< (-- r ,u) 0) `#,x))
us xs))))))
To be honest, this macro doesn't benefit much from hygiene, since you still need to call 'uniq. However, if I were to write this, I'd do it by putting the biasing logic in a function and wrapping it in a macro, which considerably reduces the amount of wrangling you have to do to get it to work with my system:
(def bias-elt (weight lst)
(let r (rand (apply + (map weight lst)))
(catch:each e lst
(if (< (-- r weight.e) 0) throw.e))))
(mac bias args
#`((cadr:bias-elt car
(list ,@(mapeach (w x) pair.args
`(list #,w (fn () #,x)))))))
To implement (2), what you do is quite simple, and it's obvious why it works, but it's a bit nonintuitive:
Oops, my example for (2) is broken. I see why it doesn't work, and it's rather interesting. It doesn't work because '++ is a _macro_ that expands to '(= <counter> (+ 1 <counter>)), where <counter> is the syntactic closure of 'counter. Then '= in turn gets macroexpanded, but '= does its own examination of the code to determine what to do (because of setter functions, etc) and it isn't written with closures in mind. I may need to look into this.
This does, however, serve to highlight the main problem with adding hygiene this way: it adds a new kind of data structure (closures) to what is considered "code", and anything that manipulates could need to be updated to handle this new case.
Wow, that's confusing. I was hoping that #` and #, could fully replace ` and , (at least in macros), so you don't have to juggle the two kinds of quasiquote.
Would it be possible to write an mappend that joins hygienically-quoted lists? It would return an ordinary list with each element inside a closure. Then we could write the first example without using the ordinary quasiquote.
Ok, I think I get it now. If we gave hygienically-quoted expressions to mappend, they would be in a different closure than the surrounding code and therefore couldn't see the variables it introduced (r, in our case). So I guess you might say that #` should usually be used at the start of a macro, and ` after that. Still confusing.
It's true, it's not quite as intuitive as I'd like it to be: I, too, originally envisioned it entirely replacing normal quasiquoting for macros, but this turned out more difficult than I expected. It might be possible to make it more intuitive, but at the moment I'm not really working on it; this is an after-the-fact explanation more than a design document. I'm presently in the process of creating a low-level systems programming language with Arc (or at least a Lisp) as its metalanguage; when I get the the problem of adding macros to that language I'll probably give this another look.
This is long overdue, but I've finally posted an explanation of the implementation of my hygienic macro system for arc (http://arclanguage.org/item?id=8599). It's written from a general lisper's perspective, because the principles involved aren't arc-specific. It doesn't yet cover the modifications to the internals of ac.scm.
Allowing a macro to know what lexical variables are bound in its calling environment is perfectly possible, though it would require some modification to ac.scm. In order to translate arc symbols into mzscheme symbols, the compiler already keeps track of what variables are locally bound. So you'd need to modify the compiler so that this list gets passed in as a "hidden parameter" to macros, and make a special form to access it. However, I'd advise against implementing this, because there's an even more unsolvable problem. Even if you fixed it, the following would still break:
The intended meaning of [square x] here is (fn () (square x)). But because 'square is not bound at the time of [square x]'s macroexpansion, even if you did have the more "intelligent" version of 'make-br-fn, it would end up as (fn (square) (square x)). At present, of course, it ends up as (fn (square x) (square x)).
Hmmm. So how about looking for only unbound single letter symbols? That would cut out all of the predefined functions, and still provide usability (I wasn't going to use it with anything more than x y z and a b c anyway.
If not, I guess there's no point in continuing to pursue this idea, is there ;)
Maybe we could look at only single letter symbols, or symbols that start with _. That would give compatibility with (most) of the current uses; the only times that it wouldn't work would be when it was used in a function that had a single letter parameter. So, maybe I should just give up then ;) Even though I don't really like _1 _2, etc., I guess it's the most viable option.
Even the 'eval thing wouldn't work. 'eval evaluates its argument at top-level, so the function would no longer be lexically scoped within its environment. You wouldn't be able to write anonymous closures anymore, just anonymous toplevel functions. Moreover, 'bound checks whether its argument is bound at global scope.
'fns is nice, but I'd like to be able to pass it symbols, not just strings. (Just run 'string on the argument.) I'll push this to anarki next chance I get, unless there are objections.
It would also probably be nicer if passing the 'test argument didn't make the 'pfx argument irrelevant - it's not really an optional argument, it's a bimodal function. Perhaps just testing whether the 'pfx argument was a function and using it as test if it were, and otherwise using [prefix string.pfx _]? This is a bigger change, though, and I won't push it unless people think it's a good idea.
I've done most of that, but I haven't pushed it yet. The only thing different is that I made pfx optional too, so that it would return all functions if you called fns with no args.
Another difference is that I made fns a macro so that it would quote the symbol passed to it, instead of evaluating it. Maybe that's what you mean. Unfortunately, the scheme of taking in bare symbols as prefixes is rather incompatible with the test to see if it's a function.
Right now the way you'd call it if you wanted your own function without a prefix is (fns nil [ex _]).
It should be named 'source* . Naming conventions are a good thing. Abbreviation for things like this is unnecessary. Also 'src or 'source might be a good name for a macro to get the source of a given function, much like 'help is now:
foldl and foldr exist in lib/util.arc on anarki. Unlike the above implementation, they are both tail-recursive:
(def flip (f)
"Flips the order of arguments of 'f. eg: ((flip cons) 1 2) -> (2 . 1)"
(fn (a b) (f b a)))
(def foldl (f v l)
(if l (foldl f (f v car.l) cdr.l) v))
(def foldr (f v l)
(foldl flip.f v rev.l))
Note that the order-of-arguments to the function passed to lib/util's version of foldl follows that of Haskell rather than SML. This is also the order used on Wikipedia (http://en.wikipedia.org/wiki/Foldl).
On what grounds (benchmarks, examples, etc) do you claim your implementations are faster than the original implementations? From merely looking at the code, it seems to me that map1 looks just like your fold version, inlined.
The tests weren't exhaustive, just simple comparison tests:
(time (repeat 1000 (map1 [* _ 3] '(1 2 3 4 5))))
Comparing the different implementations of map1, the one using the definition of fold (foldr) at the top of the page was the fastest, followed by Arc's version, followed by Anarki's version (using the definitions of flip, foldl and foldr in rntz's comment).
These informal tests were repeated on best and rev with the same results. I honestly don't know why it's faster.
No matter the number of tests, though, the difference is only a millisecond or so, which is negligible as the size of the input increases or if the mapped function does nontrivial work.
However, I've also tried the non-tail-recursive fold on very large lists, and it doesn't appear to blow the stack. mzscheme probably does something to prevent this from happening, like allocating "stack" frames on the heap (a standard technique for implementing call/cc without stack-copying, and if your GC is good it's not much of a penalty). Given this, it seems more defensible that arc.arc's 'map1 et al are non-tail-recursive. I think I may change lib/util's foldr to use the OP's implementation.
This is vaguely similar to 'defmulti in "lib/multi.arc" on Anarki (http://arclanguage.org/item?id=8330), except this allows one to override existing functions, and explicitly has in-order overriding semantics rather than look-up semantics. Similar code, different use-cases. The label argument makes it a nice improvement over anarki's 'redef, though. Personally, I think the ultimate solution to this problem is to unify pattern-matching and inheritance (http://www.rntz.net/post/oo-and-pattern-matching.html), but this is a pretty neat hack.