Aha, I finally realized that with respect to my specific complaint of obj expanding into uses of atomic, there's no need to protect the setting of the values in the table with atomic since the newly created table will not be visible to other threads at least until obj returns. The fix is easy:
A third perspective is that the bug isn't in the use of =, but that obj evaluates its value arguments too late.
In a function call
(f (g) (h))
g and h are evaluated before f is called. A user might expect obj to work the same way for its value arguments (though, as a macro, of course it doesn't have to):
(obj a (g) b (h))
This expectation can be fulfilled by evaluating the value arguments before storing them in the table. The existing macro could be modified to do that, though perhaps this implementation would be simpler: