Great idea. Like my deftimed (http://arclanguage.org/item?id=11556) this kind of 'codegenerate def' macro has one major limitation: the def can no longer have optional arguments. Anybody have ideas on how to fix that?
arc> (extract-arg-names 'xs)
(xs)
arc> (extract-arg-names '(x))
(x)
arc> (extract-arg-names '(a b))
(a b)
arc> (extract-arg-names '(a b (c d)))
(a b c d)
arc> (extract-arg-names '(a b c (d e (f g) (o h "foo")) (o i a) (o j 1)))
(a b c d e f g h i j)
So using 'extract-arg-names instead of 'flat removes the problem of optional args. Use this function whenever you have a macro that needs to manipulate parameter lists!
arc> (def myothermap (f (o xs '(a b c)))
(map f xs))
#<procedure:zz>
arc> (def otherfoo (bar)
(myothermap bar))
#<procedure: otherfoo>
arc> (otherfoo nil)
Error: "error in otherfoo(nil)\nerror in myothermap(nil (a b c))\nFunction call on inappropriate object nil (a)"
Looks like conanite beat me to it, but I whipped up a solution too:
(def proper (lst)
(accum acc
(while acons.lst
(do.acc pop.lst))
only.acc.lst))
(def o-flat (lst)
(mappend [if (caris _ 'o) (list cadr._) ; optional
alist._ flat._ ; destructuring
list._] ; normal
proper.lst)) ; turn rest into normal
; This returns a two-element list containing a list of all the cars of
; the list and the final cdr of the list. For instance,
; (iso (split-end '(a b . c)) '((a b) c)). For proper lists, it's the
; same as (split lst len.lst).
(def split-end (lst)
(let onset (accum acc
(while acons.lst
(do.acc pop.lst)))
(list onset lst)))
(def antidestruct (struct)
(treewise (fn _ `(cons ,@_)) idfn struct))
(def recreate-call (name arglist)
(withs ((nonrest rest) split-end.arglist
recreated-nonrest
(map [if (caris _ 'o) cadr._
alist._ antidestruct._
_]
nonrest))
`(apply ,name ,@recreated-nonrest ,rest)))
There are really two cases here, and none of the utility functions are used for both. The error message could be modified to use 'recreate-call too (in which case 'o-flat and 'proper would be orphaned), but that would change its behavior with regards to rest args and destructuring args, which already work. (Incidentally, I think conanite and I gave equivalent solutions for the error message case.)