closures

Discussion of Common Lisp
Post Reply
MaartenM
Posts: 5
Joined: Tue Jul 15, 2008 7:55 am

closures

Post by MaartenM » Wed Jul 16, 2008 9:43 am

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?

dmgenp
Posts: 13
Joined: Sat Jun 28, 2008 10:15 am

Re: closures

Post by dmgenp » Wed Jul 16, 2008 10:38 am

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.

qbg
Posts: 64
Joined: Mon Jun 30, 2008 1:05 pm
Location: Minnesota

Re: closures

Post by qbg » Wed Jul 16, 2008 10:54 am

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. :)

findinglisp
Posts: 447
Joined: Sat Jun 28, 2008 7:49 am
Location: Austin, TX
Contact:

Re: closures

Post by findinglisp » Wed Jul 16, 2008 12:52 pm

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/

danb
Posts: 35
Joined: Sat Jun 28, 2008 1:05 pm
Location: Urbana, Illinois, US
Contact:

Re: closures

Post by danb » Wed Jul 16, 2008 3:20 pm

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.

luskwater
Posts: 3
Joined: Tue Jul 08, 2008 5:29 am
Location: Philadelphia, PA, USA
Contact:

Re: closures

Post by luskwater » Thu Jul 17, 2008 9:18 am

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.

dlweinreb
Posts: 41
Joined: Tue Jul 01, 2008 5:11 am
Location: Lexington, MA
Contact:

Re: closures

Post by dlweinreb » Fri Jul 18, 2008 5:12 am

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.

Post Reply