Conditionally extend a function

(mac extend (name arglist test . body)
  (w/uniq args
    `(let orig ,name
       (= ,name
          (fn ,args
            (aif (apply (fn ,arglist ,test) ,args)
                  (apply (fn ,arglist ,@body) ,args)
                  (apply orig ,args)))))))

extend redefines an existing function name. When the function is now called, first the code in test is run. If the test returns nil, the original function definition is called as if the function had never been extended. But if the test succeeds, the code in body is run instead of the original function.

For example, here is an extension to + to combine tables:

(extend + args (isa (car args) 'table)
  (listtab (apply + (rev:map tablist args))))
arc> (+ (obj a 1) (obj b 2) (obj a 3 c 4))
#hash((b . 2) (c . 4) (a . 1))

The body is only called if the test “(isa (car args) 'table)” succeeds. If the test fails, then the original + is called.

Within the body which is called if the test succeeds, orig refers to the original function definition, and it refers to the value returned by the test.

Both the test and the body see the arguments specified by arglist. For example,

arc> (def foo (x)
       (+ x 5))
#<procedure: foo>
arc> (extend foo (x) (even x)
       (+ x 1))
#<procedure: foo>
arc> (foo 1)
arc> (foo 2)

Get this hack

Download extend0.arc.

Or, using the hackinator:

hack \


This code may be redistributed and modified under the same terms as Arc itself.

Contact me

Twitter: awwx
Email: andrew.wilcox [at]