I have something like this for obj, actually, which I use for creating structure-like tables:
(mac nobj args
" Creates a table from the list of variables passed to it; each variable's
name is keyed to its value. E.g. if x = 10 and y = -10, then (nobj x y)
results in #hash((y . -10) (x . 10)).
See also [[obj]] [[table]] "
`(obj ,@(mappend [list _ _] args)))
This has come up before; see particularly http://arclanguage.org/item?id=6759 . The reason you can't just do this is that Arc supports list destructuring on variable binding; to wit,
Thus, even if the first argument is a list, let has well-defined semantics. An alternative in the thread I linked to is to remove the implicit do (either modifying let or creating a new macro given), which never requires parenthesizing the arguments but requires an explicit do if its body contains more than one statement; I still think is is a good idea.
Your first version is the one currently implemented by ppr.arc. If you would like, you can write the indentation function for your second version (it can even include the "; else")
Otherwise I think that the first version is generally clearer, and the second is rarely needed, as the value of the case statement can't usually be very long. Could you give me an example where you use your second version?
Sure; here's something from my tagged-unions.arc on Anarki (Arc 2). It's responsible for parsing the types specified for slots in the tagged union:
(each type-frag types
; Assemble the ('type name) pairs into two lists, so that we can iterate
; through them.
(case (type type-frag)
; If it's a symbol, then we're defining a new name; add a new set of
; values, and go.
sym
(do
(zap [cons type-frag _] names)
(zap [cons nil _] values))
; Otherwise, we're adding a value to an existing variant.
cons
; I changed my mind about the order (now it's (name pred?) instead of
; (pred? name)), so I'm reversing it here.
(zap [cons (rev type-frag) _] (car values))
; else
(err "Invalid member of tagged union declaration.")))
I should also add (just as another data point) that my multi-condition ifs look like that too, and single-condition ifs look like the following:
(def macexn (n expr)
" Macroexpand `expr' `n' times. NB: `expr' *is* evaluated!
See also [[macex1]] [[macex]] "
(if (and (number n) (> n 0))
(macexn (- n 1) (macex1 expr))
expr))
I'm not hugely wedded to any of my conventions, though, and I haven't coded in Arc for quite a while anyway; this isn't intended to get you to change anything or convert people to my style. As I said, it's just another data point.
Anarki, at least, supports multi-character ssyntaxes, as I committed a .. ssyntax for range. The pos thing is still a good idea.
As you saw, bad ssyntax crashes because empty parts come out as #<eof>, as you saw; why that happens, I can't tell you. But I can tell you why you can't write things like a!(b c) or pr."test". The reason is that ssyntax (symbol syntax) isn't directly part of the read procedure. Structures like (tab!frob xs.ix) are read in as-is; after this, every symbol is traversed and possibly expanded, producing something like ((tab 'frob) (xs ix)). Thus, a!(b c) is read in as the two objects a! and (b c), and a! is then expanded to (a '#<eof>); similarly, pr."test" is read in as pr. "test", and then becomes (pr #<eof>) "test". Granted, those #<eof>s shouldn't be there---probably a syntax error should be produced instead---but that's what's going on.
So, maybe an arc implementation of the reader is in order? Then we could have arc level implementation of reader macros too, instead of just ssyntaxes and symbol macros.
So far, we have lib/arc-read.pack and lib/parser.arc, both in anarki, as well as lib/parsecomb.arc. Not only do you have your reader, but you have a choice of them too!
arc-read seems to be designed for easy extensibility although I haven't tried it at all. (If I had looked carefully before embarking upon parser.arc, I might have saved myself a lot of trouble).
parser.arc doesn't support the full range of scheme numbers, nor does it support |foo| symbol syntax yet, apart from ||. I'm working on a new version that will hopefully be faster - welder depends on the tokeniser for syntax highlighting.
parsecomb.arc, if I understand correctly, isn't an arc parser but a library for building parsers.
In any case, having an implementation of 'read in arc makes a lot of sense, and it's in the todo at the top of arc.arc
(mac redef (name parms . body)
" Redefine a function. The old function definition may be used within
`body' as the name `old'. "
`(do (tostring
(let old (varif ,name nilfn)
(= ,name (fn ,parms ,@body))))
,name))
It's the same as yours, except (a) it suppresses the warning on re-assigning an identifier, and (b) it calls the original function old.
The behaviour of the [] syntax makes perfect sense, if you think about it. First, [...] is just an abbreviation for (fn (_) ...) to save typing.[1] Second, `(...) is just an abbreviation for (quasiquote '(...)). Thus [`(...)] is [(quasiquote ...)], which is (fn (_) ((quasiquote ...)). If [] stripped off the outer layer of parentheses, then [+ _ 1] would become (fn (_) + _ 1), which is clearly wrong. Thus, we see that it makes more sense to use fn, since the whole point of [] and ` is to save typing and make code clearer, neither of which they can do here.
Another, possibly nicer, way to think about it is that [] just represents a delayed function call; you're trying to delay an object, and since [] is a function call, you're calling it instead. Either way, the point is that this is the consistent and desirable way for [] to work.
[1]: On Anarki, you also get _1, _2, etc., and __ for varargs, but that's not important for this discussion.
What about a special syntax (for this one situation), where you can put the backquote thus:
[` = ,_ ,form]
which would read-macro-expand to
(fn (_) `(= ,_ ,form))
I guess it's just a question of how common this problem is, and how difficult it would be to implement this type of read-macro. I guess brackets.scm goes on my list of files to check out...
That's not the case; apply doesn't care how many arguments the function takes. Take map (which takes a mandatory argument and a rest parameter) and map1 (which takes two mandatory arguments):
arc> (help map)
(from "arc.arc")
[fn] (map f . seqs)
Applies the elements of the sequences to the given function.
Returns a sequence containing the results of the function.
See also [[each]] [[mapeach]] [[map1]] [[mappend]] [[andmap]]
[[ormap]] [[reduce]]
arc> (map list '(1 2 3) '(4 5 6))
((1 4) (2 5) (3 6))
arc> (apply map list '((1 2 3) (4 5 6)))
((1 4) (2 5) (3 6))
arc> (apply map (list list '(1 2 3) '(4 5 6)))
((1 4) (2 5) (3 6))
arc> (help map1)
(from "arc.arc")
[fn] (map1 f xs)
Return a sequence with function f applied to every element in sequence xs.
See also [[map]] [[each]] [[mappend]] [[andmap]] [[ormap]]
arc> (map1 [* 2 _] '(1 2 3))
(2 4 6)
arc> (apply map1 (list [* 2 _] '(1 2 3)))
(2 4 6)
As long as the last argument to apply is a list, you're golden.
Interesting. I was wondering if that might be the case.
So I guess that we don't need an anon. macro for that ;)
I'm also wondering whether we would ever actually want an anonymous macro, since often they turn out to be so general purpose that you might want to make a utility out of it.
I'm not really clear on what an anonymous macro would do. Transform code? If we're writing it in one place, we can just transform it right there. Can you give a usage example?
And no, Arc only has ordinary macros and ssyntax. Though actually---and this just occurred to me now---you can use ssyntax to create symbol macros. For instance, I have the following in a library file
The first two let me write (@func arg1 restargs) instead of (apply func arg1 restargs) and ($func xs) instead of (map xs). However, this $ makes the $ macro misbehave, and so I added ssyntax turning @ into apply and $ into the original $ macro. It turns out that this technique is fully generic, since ssyntax essentially does a find-and-replace on symbols:
arc> (= defsym add-ssyntax-top)
#3(tagged mac #<procedure>)
arc> (defsym RANDOM (rand))
t
arc> RANDOM
0.5548165450808223
arc> RANDOM
0.15745063842035198
arc> (= things '(alpha beta gamma))
(alpha beta gamma)
arc> (defsym THING1 (car things))
t
arc> THING1
alpha
arc> (= THING1 'one)
one
arc> things
(one beta gamma)
arc> (defsym NOISY (do (prn 'HI) nil))
t
arc> NOISY
HI
nil
arc> (car NOISY)
HI
nil
This is actually really nifty, even if it's an unintended side effect. On some level, though, I'm not fully convinced of the value of this---it seems a bit omnipresent. Then again, Common Lisp works fine with them, so perhaps they're fine for Arc too. Are there naming conventions for symbol macros in Common Lisp? I used capital letters above just because I wanted some distinguishing mark.
There are a couple of caveats: first, every time you run add-ssyntax-top, you'll add another entry to the ssyntax table instead of overwriting the old one, so redefinition will make ssyntax slower if it's done too much. Second, the five tokens R, r, L, l, and ... are all unavailable, even if quoted; they turn the given string into modifying ssyntax, not standalone ssyntax. Still, this is a new option I hadn't thought about yet.