A common problem in Arc is that macros - even well-designed ones - can introduce subtle bugs. I thus propose the addition of a new axiom, a special form called symeval
.
For example, consider a library which has a complicated global function:
(def cplx-fun (sym trap val)
(some-complex-expression sym trap val))
Now the library designer intends to use this as a somewhat-internal function; what he or she expects people to use is a macro that wraps the call:
(mac cplx (val . body)
(w/uniq sym
`(cplx-fun ',sym (fn () ,@body) val)))
And of course, all is well and good... the library was so useful and powerful that people no longer felt the need to actually look inside the library and bother with how it was implemented.
Until the day, of course, where some random newbie decided to do this:
(def my-fun (x)
(let cplx-fun (fn (tmp) (cplx 42 tmp))
(cplx answer
(cplx-fun x))))
Unfortunately, the global 'cplx-fun
was overridden by the local version. So now the newbie had to bother with how the library worked, when it was already so good and perfect that nobody should have had to.
symeval
I thus propose the addition of a new axiom, 'symeval
. symeval
is a special form, like fn
or if
, and thus cannot be overridden at all.
symeval
will evaluate its argument, and check if the result is a symbol. If it's a symbol, it evaluates the symbol in the global environment.
As an optimization, the implementation can treat something of the form (symeval 'foo)
as being equivalent to an ordinary global variable read; the important thing is that (symeval 'foo)
will always read the global variable regardless of the existence of any foo
variable in the same context.
This should be trivial to add in arc2c and hence in SNAP: we need only to replace symeval
forms (probably in xe
) with global variable references if the form is quoted, and transform it into a primitive otherwise.
Thus the macro is now:
(mac cplx (val . body)
(w/uniq sym
`(symeval!cplx-fun ',sym (fn () ,@body) val)))