"It truly does not make any sense to me why zap is defined like this"
'zap is the only use I typically have for the 'setforms "binds" list (which ensures the subexpressions of 'place are only evaluated once).
Still, 'zap doesn't need to work that way: as long as I'm using a language where I know 'zap evaluates its place twice, I'm pretty much okay with it. It's a wart, but it's not an impediment.
To explore Arc-3.1-like options for a bit, here's a cleanup of Arc 3.1's definition of 'zap:
(mac zap (op place . args)
(with (gop (uniq)
gargs (map [uniq] args)
(binds val setter) setforms.place)
`(atwiths (,@binds ,gop ,op ,@(mappend list gargs args))
(,setter (,gop ,val ,@gargs)))))
If we allow 'setter and 'val to compile and evaluate before 'op and 'args, it gets shorter:
(mac zap (op place . args)
(let (binds val setter) setforms.place
`(atwiths ,binds
(,setter (,op ,val ,@args)))))
I prefer to arrange the compilation and evaluation orders from left to right ('op, 'place, 'args), using a technique like this:
(mac place (place)
(let (binds val setter) setforms.place
`(withs ,binds
(list (fn () ,val) ,setter))))
(mac zap (op place . args)
`(atomic:fn-zap ,op (place ,place) (list ,@args)))
(def fn-zap (op (getter setter) args)
(setter:apply op (getter) args))
"'zap is the only use I typically have for the 'setforms "binds" list (which ensures the subexpressions of 'place are only evaluated once)."
Hm... yes, you're right, `(zap + (foo (bar qux)) 1)` evaluates `(bar qux)` twice, and I don't see an easy/obvious way to fix that in `=`. I'll need to think about this.