4

These are two related questions actually.

  1. What is the difference between flet and let for binding functions.
  2. Are the examples A and B below equivalent?

A)

(flet ((myfun (x) (+ x 3)))
  (myfun 3))

B)

(let ((myfun (lambda (x) (+ x 3))))
  (funcall myfun 3))

NB:

(let ((myfun (lambda (x) (+ x 3))))
  (myfun 3))

will throw an error "myfun" undefined operator.

sds
  • 58,617
  • 29
  • 161
  • 278
marleynoe
  • 121
  • 3

2 Answers2

4

Let's start from your second question:

  1. Are the examples A and B below equivalent?

As the answer of @sds discusses in detail, both are semantically equivalent, even if A is compiled more efficiently by most Common Lisp compilers (and this can be easily understood because B requires an additional call to the high level function funcall).

Now, for your first question:

  1. What is the difference between flet and let for binding functions.

One difference is that, since Common Lisp is a Lisp-2 language (see for instance this question), the example A binds the name of function myfun to a functional object, while the second one bind a normal lexical variable to the same functional object.

The consequence is that, from a practical point of view, a part from efficiency, there are differences both in syntax (a more convenient way for calling the functional object, like your example shows), and in semantics (for instance, the function defines a named-block, so that one can write something like: (flet ((myfun (x) (when (< x 0) (return-from myfun 0)) (+ x 3))) (myfun -4)), not possible with a let).

But maybe the most important point is that flet is only a part of the twin special operators flet, labels to define local functions (together with macrolet to define local macros, see the specification). And with the labels operator you can define recursive local functions, which is not possible with let.

Community
  • 1
  • 1
Renzo
  • 26,848
  • 5
  • 49
  • 61
  • You would have to explicitly make a `block` in an anonymous lambda to enable an early `return-from`, so it is not impossible but inconvenient. – Svante Jan 13 '16 at 21:18
3

let creates new variable bindings.

flet define local functions.

They serve different purposes.

Your examples do the same thing, but they might be compiled differently (example A is more "orthodox" and thus is more likely to be more efficient). E.g., in CLISP:

[1]> (disassemble '(flet ((myfun (x) (+ x 3))) (myfun 3)))

Disassembly of function :LAMBDA
(CONST 0) = 3
(CONST 1) = #<COMPILED-FUNCTION :LAMBDA-MYFUN>
0 required arguments
0 optional arguments
No rest parameter
No keyword parameters
4 byte-code instructions:
0     (CONST&PUSH 0)                      ; 3
1     (CONST 1)                           ; #<COMPILED-FUNCTION :LAMBDA-MYFUN>
2     (CALLC)
3     (SKIP&RET 1)
NIL
[2]> (disassemble '(let ((myfun (lambda (x) (+ x 3)))) (funcall myfun 3)))

Disassembly of function :LAMBDA
(CONST 0) = #<COMPILED-FUNCTION :LAMBDA-1>
(CONST 1) = 3
0 required arguments
0 optional arguments
No rest parameter
No keyword parameters
5 byte-code instructions:
0     (CONST&PUSH 0)                      ; #<COMPILED-FUNCTION :LAMBDA-1>
1     (LOAD&PUSH 0)
2     (CONST&PUSH 1)                      ; 3
3     (FUNCALL 1)
5     (SKIP&RET 2)
NIL

or SBCL:

*  (disassemble (lambda () (flet ((myfun (x) (+ x 3))) (myfun 3))))

; disassembly for (LAMBDA ())
; Size: 22 bytes. Origin: #x1003BD6D94
; 94:       498B4C2460       MOV RCX, [R12+96]                ; thread.binding-stack-pointer
                                                              ; no-arg-parsing entry point
; 99:       48894DF8         MOV [RBP-8], RCX
; 9D:       BA0C000000       MOV EDX, 12
; A2:       488BE5           MOV RSP, RBP
; A5:       F8               CLC
; A6:       5D               POP RBP
; A7:       C3               RET
; A8:       CC10             BREAK 16                         ; Invalid argument count trap
NIL
*  (disassemble (lambda () (let ((myfun (lambda (x) (+ x 3)))) (funcall myfun 3))))

; disassembly for (LAMBDA ())
; Size: 34 bytes. Origin: #x1003C40804
; 04:       498B4C2460       MOV RCX, [R12+96]                ; thread.binding-stack-pointer
                                                              ; no-arg-parsing entry point
; 09:       48894DF8         MOV [RBP-8], RCX
; 0D:       BA06000000       MOV EDX, 6
; 12:       488B0597FFFFFF   MOV RAX, [RIP-105]               ; #<FUNCTION (LAMBDA
                                                              ;                #) ..>
; 19:       B902000000       MOV ECX, 2
; 1E:       FF7508           PUSH QWORD PTR [RBP+8]
; 21:       FF60FD           JMP QWORD PTR [RAX-3]
; 24:       CC10             BREAK 16                         ; Invalid argument count trap
NIL

As you can see, the flet version is more efficient.

sds
  • 58,617
  • 29
  • 161
  • 278