I'd have used
(funcall (ecase operation (:add '+) (:subtract '-) (:multiply '*)) result value)
instead, looks funkier =) (funcall (ecase operation (:add '+) (:subtract '-) (:multiply '*)) result value)
instead, looks funkier =) [1]> (setf (symbol-function :add) (function +))
#<SYSTEM-FUNCTION +>
[2]> (setf (symbol-function :subtract) (function -))
#<SYSTEM-FUNCTION ->
[3]> (setf (symbol-function :multiply) (function *))
#<SYSTEM-FUNCTION *>
[4]> (setf (symbol-function :divide) (function /))
#<SYSTEM-FUNCTION />
[5]> (reduce (lambda (x y) (funcall (car y) x (cadr y))) '((:add 5) (:multiply 3) (:subtract 4)) :initial-value 0)
11
This is actually a small benefit of the two namespaces. Keywords can't be variables because they evaluate to themselves, but that is not relevant to the operator position that does not resolve variables.But this made me realize there's no way in CL (even with PROGV shenanigans) to temporarily bind the function value of a symbol, unlike the variable one through a simple LET =(
So this turns in this horrible (and incorrect, since this doesn't restore the previous FDEFINITIONs) thing when you want to use this hack "properly":
(defun calculate (instructions)
(setf (symbol-function :add) #'+
(symbol-function :subtract) #'-
(symbol-function :multiply) #'*)
(unwind-protect
(reduce
(lambda (result op-value)
(funcall (car op-value) result (cadr op-value)))
instructions
:initial-value 0)
(fmakunbound :add)
(fmakunbound :subtract)
(fmakunbound :multiply)))Pascal Costanza somehow implemented such a thing. See 2003 paper "Dynamically Scoped Functions as the Essence of AOP", a precursor to work on AspectL and ContextL.
https://dl.acm.org/doi/pdf/10.1145/944579.944587
Dynamically scoped functions are wrappers which indirect to lambdas bound to a special variable; i.e. this is boostrapped out of dynamic variable scope.