This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

closures

7 posts · 7402 views

I am just learning Lisp. My first reaction when I saw this..:
? (let ((counter 0))
    (defun counter-next ()
      (incf counter))
    (defun counter-reset ()
      (setq counter 0)))
COUNTER-RESET

? (counter-next)
1
? (counter-next)
2
? (counter-next)
3
? (counter-next)
4
? (counter-reset)
0
? (counter-next)
1
..was something to the effect of: holy shit!

Is this good Lisp code? Is this an inherent part of the Lisp Way? Would it be wise for me to start thinking about closures in this form of manner and try to come up with nifty implementations, or should I best forget I ever saw this?

Re: closures

Personally, i don't strive to always write my code 'the lisp way'. I try to write code the right way.
If creating closures with side effects solves my problem efficiently and elegantly, i'd do it.
If my code is functional, i won't pollute it with side effects.

Re: closures

Closures can be powerful, but typically you wouldn't see top level functions be closures; in this example, what if you wanted two counters? You could do something like this:
CL-USER 12 > (defun make-counter ()
              (let ((count 0))
                (lambda (operation)
                  (case operation
                    (increment (incf count))
                    (reset (setf count 0))
                    (otherwise (error "Unknown operation ~a" operation))))))
MAKE-COUNTER

CL-USER 13 > (defvar *foo* (make-counter))
*FOO*

CL-USER 14 > (funcall *foo* 'increment)
1

CL-USER 15 > (funcall *foo* 'increment)
2

CL-USER 16 > (funcall *foo* 'reset)
0

CL-USER 17 > (funcall *foo* 'increment)
1

CL-USER 18 > (funcall *foo* 'blah)

Error: Unknown operation BLAH
  1 (abort) Return to level 0.
  2 Return to top loop level 0.

Type :b for backtrace, :c <option number> to proceed,  or :? for other options

CL-USER 19 : 1 > :c 2
This also shows that closures are a poor man's objects and objects are a poor man's closures. :)

Re: closures

qbg wrote:This also shows that closures are a poor man's objects and objects are a poor man's closures. :)
Exactly. There are times when closures are the wicked-cool, just-right-for-the-problem solution. And they are very helpful for a variety of problems. But when I really want an object, I typically just use CLOS. While the two are the poor-man's inverse of each other, there is a time and place for both.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: closures

qbg wrote:what if you wanted two counters? You could do something like this:
CL-USER 12 > (defun make-counter ()
              (let ((count 0))
                (lambda (operation)
                  (case operation
                    (increment (incf count))
                    (reset (setf count 0))
                    (otherwise (error "Unknown operation ~a" operation))))))
MAKE-COUNTER
Or, at compile time:
(defmacro defcounter (name)
  (let* ((namestr (symbol-name name))
         (next  (intern (concatenate 'string "NEXT-"  namestr)))
         (reset (intern (concatenate 'string "RESET-" namestr))))
    `(let ((counter -1))
      (defun ,next  () (incf counter))
      (defun ,reset () (setf counter -1)))))

(defcounter foo)
(defcounter bar)
To the OP,
MaartenM wrote:Is this good Lisp code? Is this an inherent part of the Lisp Way?
Yes, it's fine.
Would it be wise for me to start thinking about closures in this form of manner and try to come up with nifty implementations, or should I best forget I ever saw this?
No, don't forget it. Use it when it's useful, don't use it when it's not useful.
--Dan B.

Re: closures

Well, I just recently saw this somewhere....ah, the config.lisp file for SBCL, or CLISP, or CMUCL...not sure which at the moment.
(let ((cache nil))
  (defun long-site-name ()
    (if cache cache
	(setq cache
	      (some-lengthy-calculation-with-IO-from-shell)))))        
so there's an idiom for regular use of closures and top-level functions.

Re: closures

There's nothing wrong with your code.

However, what you're doing looks a lot like object-oriented programming. As was pointed out, closures can sort of be used as a poor person's OOP mechanism. But, using Common Lisp, you are a rich person: you have CLOS!

There are plenty of other uses for closures that are less like OOP. Often you can use either, but one of them usually ends up being more clear and stylish, reflecting the programmer's intent more clearly.