Page 1 of 1

closures

Posted: Wed Jul 16, 2008 9:43 am
by MaartenM
I am just learning Lisp. My first reaction when I saw this..:

Code: Select all

? (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

Posted: Wed Jul 16, 2008 10:38 am
by dmgenp
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

Posted: Wed Jul 16, 2008 10:54 am
by qbg
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:

Code: Select all

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

Posted: Wed Jul 16, 2008 12:52 pm
by findinglisp
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.

Re: closures

Posted: Wed Jul 16, 2008 3:20 pm
by danb
qbg wrote:what if you wanted two counters? You could do something like this:

Code: Select all

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:

Code: Select all

(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.

Re: closures

Posted: Thu Jul 17, 2008 9:18 am
by luskwater
Well, I just recently saw this somewhere....ah, the config.lisp file for SBCL, or CLISP, or CMUCL...not sure which at the moment.

Code: Select all

(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

Posted: Fri Jul 18, 2008 5:12 am
by dlweinreb
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.