| Arc expands macros before ssyntax. Your macros' arguments may be or include unexpanded ssyntax. As the macro writer, you can decide what to do with these symbols, and you don't have to leave it to ac to decide what to do. Given that every solution must have a great problem, here's an experiment with that idea: arc> (dfn some (testify:test seq)
(if (alist seq)
(reclist test:car seq)
(recstring test:seq seq)))
arc> (some 'z '(a b c d e))
t
arc> (some 'z '(a b c d e))
nil
arc> (some [isa _ 'sym] '(a b c))
t
arc> (some [isa _ 'int] '(a b c))
nil
This 'some is equivalent to the original from arc.arc, and I believe it wins for readability: (def some (test seq)
(let f (testify test)
(if (alist seq)
(reclist f:car seq)
(recstring f:seq seq))))
This isn't a world changing idea, but (-- tokens) and there are times when it's useful to pass your arguments through some kind of a filter. There are at least 12 other functions that use testify in this way in arc.arc and strings.arc. But regardless of whether this particular example excites you, I'm sure there are many other great ways to exploit custom macro-specific ssyntax.Here's a quick and dirty implementation, I'm sure it could be improved to effect a net decrease in total token count. Disclaimer: only lightly tested (scheme & rainbow). (mac dfn (name params . body)
`(= ,name (fn ,(dfn-params params)
(with ,(dfn-withses params)
,@body))))
(def dfn-p-mapper (p)
(if (acons p) (map2 dfn-p-mapper p)
(let toks (tokens (coerce p 'string) #\:)
(if cdr.toks
(if (is car.toks "?")
`(o ,(sym:cadr toks))
(sym:cadr toks))
p))))
(def dfn-params (params)
(map2 dfn-p-mapper params))
(def dfn-param (p)
(let toks (tokens (coerce p 'string) #\:)
(if (and cdr.toks (isnt car.toks "?"))
`(,(sym:cadr toks) (,(sym:car toks) ,(sym:cadr toks))))))
(def dfn-withses (params)
(mappend idfn (accum x
(afnwith (p params)
(if (no p) nil
(atom p) (aif dfn-param.p x.it)
(do (self:car p) (self:cdr p)))))))
; like map1, but handles improper lists
(def map2 (f xs)
(if (no xs) nil
(atom xs) (f xs)
(cons (f:car xs) (map2 f cdr.xs))))
Note that dfn uses '= to assign the function, so you can use any expression for which a setter is defined, not just a symbol. Here are some test cases: ; use ?:x instead of (o x nil)
(dfn dfn-test (string:foo ?:bar)
(pr "foo is a " type.foo " and bar is " bar))
; works for rest params too
(dfn dfn-test2 (string:foo . len:bar)
(pr "foo is a " type.foo " and there are " bar " bars"))
; identical to def if there is no param ssyntax
(dfn dfn-test3 (foo . bar)
(pr "foo is a " type.foo " and bar is " bar))
(suite "dfn: a superior def"
("use ?:x instead of (o x nil)"
(tostring:dfn-test 'hello)
"foo is a string and bar is nil")
("? for optionals: given"
(tostring:dfn-test 'hello 1234)
"foo is a string and bar is 1234")
("handles rest param"
(tostring:dfn-test2 'resting 'a 'b 'c 'd)
"foo is a string and there are 4 bars")
("without ssyntax is identical to def"
(tostring:dfn-test3 'a 'b 'c 'd)
"foo is a sym and bar is (b c d)"))
|