Search code examples
lispcommon-lispsbcl

Can I use lambda with an on-the-fly lambda list (without macros)?


I'm trying to create a function to return functions, with arbitrary lambda lists, generated on the fly. I can do it with macros, but I'm trying to de-macro-ify what I've already got:

(defmacro make-canned-format-macro (template field-names)
  `(function (lambda ,field-names
               (apply #'format `(nil ,,template ,,@field-names)))))

I can use it as follows:

* (make-canned-format-macro "~A-powered ~A" (fuel device))

#<FUNCTION (LAMBDA (FUEL DEVICE)) {10067D975B}>
* (setf (fdefinition 'zoom-zoom) (make-canned-format-macro "~A-powered ~A" (fuel device)))

#<FUNCTION (LAMBDA (FUEL DEVICE)) {1006835A5B}>
* (zoom-zoom "nuclear" "pogo stick")

"nuclear-powered pogo stick"

This is exactly the behavior I want. It returns a function whose lambda list was supplied on the fly (in this case, (fuel device).) I'm trying to do the Proper Lisp Refactoring Thing and do away with macros that don't have to be macros. However, I've gotten stuck trying to glom an arbitrary lambda list into a lambda that's getting executed in a function:

* (defun make-canned-format (template field-names)
    #'(lambda field-names (apply #'format `(nil ,template ,@field-names))))
; in: DEFUN MAKE-CANNED-FORMAT
;     #'(LAMBDA FIELD-NAMES (APPLY #'FORMAT `(NIL ,TEMPLATE ,@FIELD-NAMES)))
; 
; caught ERROR:
;   The lambda expression has a missing or non-list lambda list:
;     (LAMBDA FIELD-NAMES (APPLY #'FORMAT `(NIL ,TEMPLATE ,@FIELD-NAMES)))

;     (SB-INT:NAMED-LAMBDA MAKE-CANNED-FORMAT
;         (TEMPLATE FIELD-NAMES)
;       (BLOCK MAKE-CANNED-FORMAT
;         #'(LAMBDA FIELD-NAMES (APPLY #'FORMAT `(NIL ,TEMPLATE ,@FIELD-NAMES)))))
; 
; caught STYLE-WARNING:
;   The variable TEMPLATE is defined but never used.
; 
; caught STYLE-WARNING:
;   The variable FIELD-NAMES is defined but never used.
; 
; compilation unit finished
;   caught 1 ERROR condition
;   caught 2 STYLE-WARNING conditions

MAKE-CANNED-FORMAT

Is what I'm trying to do even possible? (Aside from some hideous eval hack that would be way less readable than the macro, I mean.)


Solution

  • To turn make-canned-format into a function, you need to replace function with compile or (coerce (lambda ...) 'function).

    However, your refactoring is misguided. make-canned-format should be a macro - this way it will produce a closure in the current compilation environment. The function, however, will produce a closure in the global environment.