The auxiliary code is responsible for finding all the variables in the code and then finding all the _\d+s (and __) that occur free in the expression. It's probably bulkier than it needs to be---I wrote it with a decent amount of class experience in writing interpreters, but without any real-world experience. The big thing you seem to be missing is expand, which will macro-expand its argument. That would result in the following change:
I've looked at expand; how does it help there? Is that necessary to expand macros inside a bracket function?
I still have no idea how to check binding at compile time; in theory it could know that it was inside a binding expression (fn) but I think that would take a lot of modification to the language, and probably isn't worth it, unless it allows other nifty features.
Aha, I see. Yes, in this case, expand probably won't help. But it's actually conceptually simple to checking binding (without eval) at compile time; that's what make-br-fns does, after all. What you do is you run expand on the source tree, then just go through and check and see if it's within a fn. This is what all the auxiliary functions for make-br-fns are doing: checking to see what's in an argument list, seeing if variables used are in argument lists, etc. Obviously this breaks if you run, say, (eval '_2), but it works in other situations.
Wait, how does it know what context it's run in? I can see checking if the symbol is bound underneath it, but what about checking to see if it is bound above? (i.e. br-fn inside a withs) How does that work?
...Oh dear :-[ It doesn't, and I hadn't noticed that until right now. Apparently, it's not a problem, presumably because nobody ever declares variables called _1 :P Yeah, I don't see a feasible way to do that, unless make-br-fns produces something like
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.
Unless there was a way for a function to examine its declaration environment. Then we could have a function that crawls up the tree looking for binding statements, and removing those from the list of unbound symbols. I just don't know how hard that would be to add. It could be useful for other things though, like better error messages. If the context were mutable, it could allow macros to do a) really cool things and b) likely very buggy things. But if people knew to expect it, it might be ok.
How would you implement a read-only context, visible during compile time? Is it even possible? In theory, the reader has already read in the other stuff, and parsed it into a list.
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.
>I don't know precisely how you're doing this, though, so that might not work.
All my source table does is
(sref source* '(def ,name ,parms ,@body) ',name)
for def and
(sref source* '(mac ,name ,parms ,@body) ',name)
for mac.
Since name, parms, and body are read by the reader, reader macros have already been expanded. I'm looking for a way to print the source in a more readable fashion.
Your functions looks pretty close to what I want. Maybe stringify could just be syntaxify, and make it recursive? I don't know if tail recursion is possible because of make-br-fn, but the rest of it looks pretty simple.
If the current object is a cons, but not one of the above, return "(" (syntaxify:cdr _) ")", and if it's an atom return (string _).
I'll do some more thinking on it, but it looks like you're on the right track.
Adding spaces shouldn't be too hard; are you saying that we don't know how to handle indentation and newlines? I don't think that should be too hard. Looking at ppr and friends in string.arc should give us a clue as to that.
Is it possible to create a list in which the syntax is not expanded? I.e. preserve our syntaxification, after we've completed it? So that ', `, , ,@ [] become part of their symbols? That would probably be helpful, because then we could just use the built in ppr function, and not have to worry about formatting.
To nitpick about notation (not about concept), on Anarki, the xy macro is unnecessary; the [] construction can construct n-ary procedures. Thus, we would have, for instance,
(def map1 (f xs)
(fold [cons (f _1) _2] nil xs))
. Your point about the benefits of fold is a good one, of course!
If you don't like _1, _2, etc. we could implement the system discussed here: http://arclanguage.org/item?id=8617. Basically, bind the unbound symbols inside the brackets in alphabetical order. It would work with _, _1, _2 as usual, but also x, y. Generally when people do short functions of a few variables they pick them in alphabetical order, so it makes sense. I'm just not sure how to go about implementing it. Presumably it would have to search the body for symbols which fail the bound predicate. sort them, and use them as the argument list. Unfortunately, it sounds like a macro with run-time knowledge. I don't know if macros have information on whether a symbol is bound or not; I would presume not.
You could look at make-br-fn on Anarki; it crawls through the [] functions finding free variables of the form it wants (_\d+ or __). It might be possible to modify it to pick everything out, if we want to. The downside, of course, is that if you typo, say, string as strnig, the [] function will think it's a brand-new variable. And the other downside is that lexically speaking, '_10 < '_2, though we could always special-case numbers.
Ok, I'll look at make-br-fn. And I hadn't thought about _10 < _2. However, when is someone going to write a 10 arg function with no names? Sounds suspicious to me. I agree that strnig would cause problems. However, unless you are very lucky, calling the arg "strnig" in function position will cause as much of an error as calling strnig in a normal function call. Of course, to see how useful it really is, I'd have to try it. I think that in most cases where all you want is three vars a, b, & c, or x, y, and z, it should work fine. And look better than _1 _2 _3, with less typing.
To answer your last question, I think that printing to stderr instead of stdout won't show up in the browser; you can use the builtin ero or the following two functions to do so
(def pre args
" Like `pr', but writes to stderr.
See also: [[prne]] [[pr]] [[ero]] "
(w/stdout (stderr) (apply pr args)))
(def prne args
" Like `prn', but writes to stderr.
See also: [[pre]] [[prn]] [[ero]] "
(w/stdout (stderr) (apply prn args)))
This has been discussed before; as CatDancer said, the problem is auto-destructuring of lists. On Anarki, there are two macros, given and givens; they don't require the variables to be parenthesized, but can only have one expression in the body (which can, of course, be a do). given is analogous to with, and givens is analogous to withs. For more info, see http://arclanguage.org/item?id=6759 .
If you're coming from Java or another such language, then anonymous functions and the REPL are must-shows. The former really makes things far more clear and concise; just compare Arc's
(let squares (map [* _ _] nums)
...)
to Java's
ArrayList<Integer> squares = new ArrayList<Integer>();
for (Integer n : nums)
squares.addObject(n*n)
...
. The latter is fantastic for development; rather than having to code up a new test, save, and recompile every time you have something you want to test out, you can iteratively develop your test and run it immediately. This becomes even more of a net win when you realize you need to change that previous test case just a little; instead of open-edit-recompile-run, it's uparrow-edit-run.
If you're instead coming from something more like a scripting language, then yeah---macros and everything they can do is the biggest win.
True. When I search for "rainbow lisp", google asks me did I mean "rainbow lips". Rainbow's github page is the 6th result for "arc rainbow", alone among many discussions of the shape of the multicoloured atmospheric phenomenon.
Once we've taken over the world, I think it will get better.
> Merit of French
I happen to live in france. It was a convenient pun. No other reason, especially not love of the language that I haven't got to grips with even after nine years of living here.