From: Antonio Leitao
Subject: Re: Functional programming
Date: 
Message-ID: <wowvrpl4e6.fsf@jamaica.gia.ist.utl.pt>
>>>>> "Rob" == Rob Warnock <····@rigden.engr.sgi.com> writes:

[...]

 Rob> Exactly. CL gives you the *choice* of binding lateness versus
 Rob> effeciency, because "symbol-function" is in the language
 Rob> standard and is relatively cheap, compared to "eval". I didn't
 Rob> mean to imply that CL *couldn't* pass closures, or that it
 Rob> wasn't usually "the right thing", but only that
 Rob> Scheme-the-standard doesn't give you a *choice* other than
 Rob> (presumably slow) "eval", so passing closures is usually
 Rob> considered the "only right thing".

I don't understand why must we use eval.

 Rob> Having said that, of course, someone will chide me about how
 Rob> specific *implementations* of Scheme give you
 Rob> "(global-defined-value <symbol>)" [which, for Scheme, a Lisp-1,
 Rob> is the closest equivalent of "symbol-function" and presumably as
 Rob> cheap] or the like, but I think we're talking mostly about the
 Rob> standard-defined languages here, yes? Implementation extensions
 Rob> don't count in such discussions.

What's the problem with this approach?

;;Defining func1

(define func1 (list (lambda () (display "do something") (newline))))

;;Defining func2

(define func2 (list (lambda () (display "do more things") (newline))))

;;Running hooks extracts the function before applying it

(define (run-hooks hooks)
  (for-each (lambda (hook)
              ((car hook)))
            hooks))

;;Let's create some hooks

(define my-hooks (list func1 func2))

;;Let's run them

> (run-hooks my-hooks)
do something
do more things

;;Let's redefine func1

(set-car! func1 (lambda () (display "I'm not doing the same thing") (newline)))

;;Let's run the hooks again

> (run-hooks my-hooks)
I'm not doing the same thing
do more things

Why do we need a specific *implementation* of scheme?  It doesn't seem
hard to define some macrology to hide the explicit use of list and
set-car!.

It doesn't seem to me that car and set-car! are less efficient than
symbol-function and (setf symbol-function).

Maybe I don't understand the problem... 

António Leitão.

From: Rob Warnock
Subject: Re: Functional programming
Date: 
Message-ID: <80eaq1$64l28@fido.engr.sgi.com>
Antonio Leitao  <···@gia.ist.utl.pt> wrote:
+---------------
| >>>>> "Rob" =3D=3D Rob Warnock <····@rigden.engr.sgi.com> writes:
|  Rob> Scheme-the-standard doesn't give you a *choice* other than
|  Rob> (presumably slow) "eval", so passing closures is usually
|  Rob> considered the "only right thing".
| 
| I don't understand why must we use eval.
+---------------

Because the original problem being discussed was hooks that are lists
of *symbols*, as is often done in Emacs & CL, not closures. That is:

	(define my-hooks (list 'func1 'func2))

Now, if (for better or worse) that's how the hook is *defined*, how
do you call those functions in *standard* Scheme without using "eval"?
[...or a non-standard extension such as MzScheme's "global-defined-value".]

+---------------
| What's the problem with this approach?
...
| (define my-hooks (list func1 func2))
...
| (define (run-hooks hooks)
|   (for-each (lambda (hook) ((car hook))) hooks))
+---------------

[Uh... You don't need the "car"; "for-each" does that for you.]

Well, your hook variable is a list of *closures*, not symbols.
That certainly "works" (for some definitions of "work"), but fails
to address two of the issues others expressed in this thread:
(1) that the traditional Lisp hook style is to use symbols, and
(2) that using symbols instead of closures has the advantage of
automatically using the *new* value of any hook function that's
redefined, even *after* being added to the hook. Compare both ways:

	> (define (foo) (print "foo"))
	> (define (bar) (print "bar"))
	> (define sym-hook (list 'foo 'bar))	; the symbol-based version
	> (define (do-sym-hooks)
	    (for-each (lambda (x) ((eval x))) sym-hook))
	> (define clo-hook (list foo bar))	; the closure-based version
	> (define (do-clo-hooks)
	    (for-each (lambda (x) (x)) clo-hook))
	> (do-sym-hooks)
	foo
	bar
	> (do-clo-hooks)
	foo
	bar
	>

The same so far, yes? Now look what happens when we redefine one
of the hook functions:

	> (define (foo) (print "this is a new, improved foo!!"))
	> (do-sym-hooks)
	this is a new, improved foo!!
	bar
	> (do-clo-hooks)
	foo
	bar
	>

Oops!!


-Rob

p.s. Yes, there are ways to get around this problem *without* using
"eval", e.g., make every hook function be a trampoline for the "real"
function, the one that gets edited:

	> (define (real-foo) (print "foo"))
	> (define (foo) (real-foo))
	> (define clo-hook (list foo bar))
	> (do-clo-hooks)
	foo
	bar
	> (define (real-foo) (print "this is a new, improved foo!!"))
	> (do-clo-hooks)
	this is a new, improved foo!!
	bar
	>

But 'tis a bit cumbersome, yes?

-----
Rob Warnock, 8L-846		····@sgi.com
Applied Networking		http://reality.sgi.com/rpw3/
Silicon Graphics, Inc.		Phone: 650-933-1673
1600 Amphitheatre Pkwy.		FAX: 650-933-0511
Mountain View, CA  94043	PP-ASEL-IA
From: Tim Bradshaw
Subject: Re: Functional programming
Date: 
Message-ID: <ey3so2dm7nd.fsf@lostwithiel.tfeb.org>
* Rob Warnock wrote:

> Well, your hook variable is a list of *closures*, not symbols.
> That certainly "works" (for some definitions of "work"), but fails
> to address two of the issues others expressed in this thread:
> (1) that the traditional Lisp hook style is to use symbols, and
> (2) that using symbols instead of closures has the advantage of
> automatically using the *new* value of any hook function that's
> redefined, even *after* being added to the hook. Compare both ways:

No, I think his hook var is a list of lists, the car of each list being a
function/closure, so (because you can clobber the car of the list),
some kind of redefinition would work.  But it wouldn't be the kind you
get in CL -- redefine the function, and the new one gets called, you'd
need a special macro or something to do this.  Which is kind of what
CL people are complaining about -- in Scheme you need to do everything
yourself!

--tim
From: Antonio Leitao
Subject: Re: Functional programming
Date: 
Message-ID: <won1slkouo.fsf@jamaica.gia.ist.utl.pt>
>>>>> "Rob" == Rob Warnock <····@rigden.engr.sgi.com> writes:

 Rob> Antonio Leitao  <···@gia.ist.utl.pt> wrote:
 Rob> +---------------
 Rob> | >>>>> "Rob" =3D=3D Rob Warnock <····@rigden.engr.sgi.com> writes:
 Rob> |  Rob> Scheme-the-standard doesn't give you a *choice* other than
 Rob> |  Rob> (presumably slow) "eval", so passing closures is usually
 Rob> |  Rob> considered the "only right thing".
 Rob> | 
 Rob> | I don't understand why must we use eval.
 Rob> +---------------

 Rob> Because the original problem being discussed was hooks that are lists
 Rob> of *symbols*, as is often done in Emacs & CL, not closures. That is:

 Rob> 	(define my-hooks (list 'func1 'func2))

 Rob> Now, if (for better or worse) that's how the hook is *defined*, how
 Rob> do you call those functions in *standard* Scheme without using "eval"?
 Rob> [...or a non-standard extension such as MzScheme's "global-defined-value".]

It's not that hard, actually.  Just use an assoc-list (or hashtable)
that relates the symbol to the function.  If that's the way you want
to define my-hooks, I would say that:

(define hook-function-table 
  (list (cons 'func1 
	      (lambda () (display "do something") (newline)))
        (cons 'func2
              (lambda () (display "do more things") (newline)))))

and

(define (run-hooks hooks)
  (for-each (lambda (hook)
              ((cdr (assq hook hook-function-table))))
            hooks))

solves the problem.

Of course, we should define a macro such as

(def-hook (func1)
  (display "do more things")
  (newline))

that takes car of updating the hook-function-table.  Again, it doesn't
look very hard.

You might argue that this approach is slower than using eval.  You can
you hashtables, if you need the extra speed, but at least you have the
usual lexically scoped evaluation.

 Rob> +---------------
 Rob> | What's the problem with this approach?
 Rob> ...
 Rob> | (define my-hooks (list func1 func2))
 Rob> ...
 Rob> | (define (run-hooks hooks)
 Rob> |   (for-each (lambda (hook) ((car hook))) hooks))
 Rob> +---------------

 Rob> [Uh... You don't need the "car"; "for-each" does that for you.]

Are you sure?  I don't think you can do that.  For-each is applied to
a list of lists.  Each of the lists contains just one function.  Each
list is in fact an handle that allows me to change the function
dynamically.  As a result, the lambda is applied to each of these
handles and must use car to extract the function.

Note that func1 was defined as:

(define func1 (list (lambda () (display "do something") (newline))))

Note the list around the lambda.

 Rob> Well, your hook variable is a list of *closures*, not symbols.

Not quite.

My hook variable was (in the version you are mentioning) a list of
handles to closures.  See above.

 Rob> That certainly "works" (for some definitions of "work"), but fails
 Rob> to address two of the issues others expressed in this thread:
 Rob> (1) that the traditional Lisp hook style is to use symbols, and

I use symbols in the second version.

 Rob> (2) that using symbols instead of closures has the advantage of
 Rob> automatically using the *new* value of any hook function that's
 Rob> redefined, even *after* being added to the hook. Compare both ways:

Again, you just need some macrology to hide the update (either to the
handle in the first version or to the assoc-list in the second version).

 >> (define (foo) (print "foo"))
 >> (define (bar) (print "bar"))
 >> (define sym-hook (list 'foo 'bar))	; the symbol-based version
 >> (define (do-sym-hooks)
 Rob> 	    (for-each (lambda (x) ((eval x))) sym-hook))


 >> (define clo-hook (list foo bar))	; the closure-based version

That's not what I was doing...

 >> (define (do-clo-hooks)
 Rob> 	    (for-each (lambda (x) (x)) clo-hook))
 >> (do-sym-hooks)
 Rob> 	foo
 Rob> 	bar
 >> (do-clo-hooks)
 Rob> 	foo
 Rob> 	bar
 >> 

It's obvious that this would not work.  But if you look carefully to
my solution you will see that it's not what you are suggesting.  Try
my solution.

 Rob> The same so far, yes? Now look what happens when we redefine one
 Rob> of the hook functions:

 >> (define (foo) (print "this is a new, improved foo!!"))
 >> (do-sym-hooks)
 Rob> 	this is a new, improved foo!!
 Rob> 	bar
 >> (do-clo-hooks)
 Rob> 	foo
 Rob> 	bar
 >> 

 Rob> Oops!!

Pretty obvious...

Look again at my previous solution, please.

Note that I'm not a Scheme fanatic (or CL fanatic, for that matter).
I just didn't understand what was so different in Scheme and CL that
would make impossible to do in Scheme something so simple as
implementing hooks.

Maybe I'm still not understanding the problem...

On the other hand, there are some things in Scheme that are extremely
difficult to implement in CL. As a result, I think that with some
extra SRFIs (maybe 20 more) Scheme will be similar to CL, (modulo
being a Lisp1) but with one added feature: first class continuations.

 Rob> p.s. Yes, there are ways to get around this problem *without* using
 Rob> "eval", e.g., make every hook function be a trampoline for the "real"
 Rob> function, the one that gets edited:

 >> (define (real-foo) (print "foo"))
 >> (define (foo) (real-foo))
 >> (define clo-hook (list foo bar))
 >> (do-clo-hooks)
 Rob> 	foo
 Rob> 	bar
 >> (define (real-foo) (print "this is a new, improved foo!!"))
 >> (do-clo-hooks)
 Rob> 	this is a new, improved foo!!
 Rob> 	bar
 >> 

 Rob> But 'tis a bit cumbersome, yes?

I agree.  I prefer my solutions.

António.
From: Matthew Economou
Subject: First-class continuations? (was Re: Functional programming)
Date: 
Message-ID: <w4og0ycvqjf.fsf_-_@nemesis.irtnog.org>
Antonio,

What exactly are "first-class continuations" in Scheme?  Semantically,
Scheme and Common Lisp's LAMBDA expressions are identical (modulo an
explicit FUNCALL in CL to get around it's Lisp-2-ness), so I assume
your talking about something other than closures in either language.
Are you talking about CALL-WITH-CURRENT-CONTINUATION in Scheme?

-- 
"I'm very busy.  I'm preparing my next mistake." -- B. Brecht
From: Antonio Leitao
Subject: Re: First-class continuations? (was Re: Functional programming)
Date: 
Message-ID: <wo66z8ls4s.fsf@jamaica.gia.ist.utl.pt>
>>>>> "Matthew" == Matthew Economou <········@irtnog.org> writes:

 Matthew> Antonio,
 Matthew> What exactly are "first-class continuations" in Scheme?
 Matthew> Semantically, Scheme and Common Lisp's LAMBDA expressions
 Matthew> are identical (modulo an explicit FUNCALL in CL to get
 Matthew> around it's Lisp-2-ness), so I assume your talking about
 Matthew> something other than closures in either language.  Are you
 Matthew> talking about CALL-WITH-CURRENT-CONTINUATION in Scheme?

Precisely.

Antonio.