5

The problem with flet is that the functions bound therein must be defined inline. In other words, there's no way to do this:

(new-flet ((a (lambda (f x)
                (funcall f (* x 2))))
           (b (function-generator)))
    (a #'b 10))

I considered defining such a macro myself, but the problem is that flet seems to be the only way to set local function values. symbol-function always gets the global definition only, and function can't be used with setf. Anyone have an idea how this can be done fairly cleanly, if at all?

Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
nbtrap
  • 581
  • 2
  • 12
  • I think you are forgetting about `labels`. – sds Aug 21 '13 at 16:42
  • I haven't forgotten about `labels`. My questions doesn't have anything to do with mutually recursive function bindings. The same problem here applies to `flet` and `labels`. – nbtrap Aug 21 '13 at 16:44
  • Sorry, I'm just too used to Scheme I guess. – nbtrap Aug 21 '13 at 16:57
  • I don't understand what your question is. Do you want to be able to override function calls inside of a function outside that function scope without using flet? Is Let and funcall what you are looking for? – PuercoPop Aug 21 '13 at 18:21

4 Answers4

5

You can easily build a trampoline

(defun function-generator (x)
  (lambda (y) (* x y)))

(let ((fg (function-generator 42)))
  (flet ((a (f x) (funcall f (* x 2)))
         (b (x) (funcall fg x)))
    (a #'b 10)))

A macro implementation of new-flet with this approach is

(defmacro new-flet (bindings &body body)
  (let ((let-bindings (list))
        (flet-bindings (list))
        (args (gensym)))
    (dolist (binding bindings)
      (let ((name (gensym)))
        (push `(,name ,(second binding))
              let-bindings)
        (push `(,(first binding) (&rest ,args)
                 (apply ,name ,args))
              flet-bindings)))
    `(let ,(nreverse let-bindings)
       (flet ,(nreverse flet-bindings)
         ,@body))))

that expands in your example case as

(macroexpand-1 '(new-flet ((a (lambda (f x) (funcall f (* x 2))))
                           (b (function-generator)))
                  (a #'b 10)))

==> (LET ((#:G605 (LAMBDA (F X)
                    (FUNCALL F (* X 2))))
          (#:G606 (FUNCTION-GENERATOR)))
      (FLET ((A (&REST #:G604)
               (APPLY #:G605 #:G604))
             (B (&REST #:G604)
               (APPLY #:G606 #:G604)))
        (A #'B 10)))
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
6502
  • 112,025
  • 15
  • 165
  • 265
  • `MAKE-sYMBOL` might be preferable. Depends. Also here one might also declare `dynamic-extent` for the new `&rest` variable. Lisp might be able to exploit that. – Rainer Joswig Aug 21 '13 at 18:03
  • Awesome. I was afraid this would be impossible because I thought the macro would have to transform every instance of `(a ...)` into `(funcall a ...)` (which wouldn't work cleanly when, e.g., `(a ...)` is itself part of another special syntactical form to be transformed later). I didn't think to just wrap the function in another. – nbtrap Aug 21 '13 at 18:04
  • @RainerJoswig What are the advantages/disadvantages to using `make-symbol` vs. `gensym`? – nbtrap Aug 22 '13 at 16:03
1

Is

(let* ((a (lambda (f x) (funcall f (* x 2))))
       (b (function-generator)))
    (funcall a b 10))

a fairly clean solution to your problem?

Terje D.
  • 6,250
  • 1
  • 22
  • 30
  • 1
    Obviously, that's not the same as `flet`, which binds the given identifiers in the function namespace. :-( – C. K. Young Aug 21 '13 at 16:28
  • @ChrisJester-Young No, it is not the same as `flet`, but it _is_ a minor rewrite that makes the code in the question work. Thus the question if it could be regarded as a _fairly clean_ solution. – Terje D. Aug 21 '13 at 19:40
  • Your solution is actually a macro-expansion of the solution I was looking for. I didn't want to have to write `funcall` all over the place--just once, in the macro definition. – nbtrap Aug 24 '13 at 15:40
1

How about binding the variables with let, so that they're setfable, and then using an flet as the body of the let so that they're funcallable and (function …)-able, too. E.g., where I've given a silly little function instead of (generate-function):

(let ((a (lambda (f x)
           (funcall f (* x 2))))
      (b (lambda (&rest args)
           (print (list* 'print-from-b args)))))
  (flet ((a (&rest args)
           (apply a args))
         (b (&rest args)
           (apply b args)))
    (a #'b 10)))

We can wrap this up in a macro relatively easily:

(defmacro let/flet (bindings &body body)
  (let ((args (gensym (string '#:args-))))
    `(let ,bindings
       (flet ,(loop :for (name nil) :in bindings
                 :collect `(,name (&rest ,args) (apply ,name ,args)))
         ,@body))))

Now

(let/flet ((a (lambda (f x)
                (funcall f (* x 2))))
           (b (lambda (&rest args)
                (print (list* 'print-from-b args)))))
  (a #'b 10))

expands into the first block of code. Note that you can also use (a b 10) in the body as well, since the binding of b is the same as the value of #'b. You can use setf on the variable as well:

(let/flet ((a (lambda (x)
                (print (list 'from-a x)))))
  (a 23)
  (setf a (lambda (x)
            (print (list 'from-new-a x x))))
  (a 23))

prints

(FROM-A 23) 
(FROM-NEW-A 23 23) 
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • The let/flet combination here is the same as the one in [6502's answer](http://stackoverflow.com/a/18363025/1281433) (which I didn't read carefully enough before writing this one), except that here the variable bindings are preserved, so that `setf` works on them, which was one of the requirements in the question. I think that the macro code is a bit easier to read, too. – Joshua Taylor Aug 21 '13 at 17:23
  • This is good, but I think 6502's answer is slightly better, since his doesn't pollute the variable namespace. Nice work nonetheless. – nbtrap Aug 21 '13 at 17:45
  • @nbtrap Does that mean you don't need to be able to `setf` the functions after all? – Joshua Taylor Aug 21 '13 at 17:57
  • Being able to setf the _local function slot_ would have enabled me to write a clean macro that I was envisioning (without messing with the variable namespace). But the solution you and 6502 provided make that unnecessary. I was thinking too shallowly. – nbtrap Aug 21 '13 at 18:08
  • @nbtrap Ah, then yes, 6502's is nice for not polluting the variable namespace. I'll leave this here in case someone _does_ want to be able to update the bindings (and, while it may just be personal taste, I still think this macro code (which can be easily modified not to pollute the value namespace) is a bit easier to follow. – Joshua Taylor Aug 21 '13 at 18:30
0

If anyone's interested in a labels equivalent, here it is:

(defmacro my-labels ((&rest definitions) &rest body)
  (let ((gensyms (loop for d in definitions collect (gensym)))
        (names (loop for d in definitions collect (car d)))
        (fdefs (loop for f in definitions collect (cadr f)))
        (args (gensym)))
    `(let (,@(loop for g in gensyms collect (list g)))
       (labels (,@(loop for g in gensyms for n in names
                     collect `(,n (&rest ,args) (apply ,g ,args))))
         ,@(loop for g in gensyms for f in fdefs
                collect `(setf ,g ,f))
         ,@body))))

This is sort of like Scheme's letrec.

nbtrap
  • 581
  • 2
  • 12