Some previous issues (1) resulted in a copy
function being added to macros.fnl
, to perform form modification hygienically.
It's probably worth formalising an interface to this, as well as documenting why it exists, so that macro writers can avoid the same mistakes.
While the function is (currently?) pretty small, and generic (it operates on any table), I think it is probably worth giving it a more descriptive name to prompt its correct usage.
I am not sure if it should be called (clone-list ...)
because it could be useful in other contexts, or perhaps limited to only the list
type?
There is also the option of making it more complicated than table-in->table-out, such as (safely-modify-list some-list (fn [the-list] (table.insert the-list 2 :argument)))
but that might not be to everyones taste.
I think including the "safely" term implies that there is a complementary "unsafe" way to do it, which sort of self-documents that you should use the function. That said, if the macro guide included some big words saying "hey if you're going to do (X f)
, do (X (copy f))
" that's maybe just as good.
Also should the copy function be deep-recursive? Not quite sure on the semantics of it in the compiler context I guess. Since it is compile time, a recursive deep copy wont have any negative runtime impact at least.
I've made a branch that has this here: https://git.sr.ht/~technomancy/fennel/commit/04333f2224df734a4c5d406a08a033de5043c205
This is a simple metatable-preserving shallow-copy for generalized tables (the original
copy
only worked on sequences and lists because it didn't usepairs
), which I think is good enough most of the time. It's quite rare that you'd have a macro that does invasive changes beyond the top level.Having a higher-order function which takes an updater function would make sense if the data were immutable, but doesn't really fit with mutable tables.
Anyway, interested in hearing your thoughts.