* Closure?
@ 2008-07-11 14:48 Maciek Godek
2008-07-11 15:01 ` Closure? Ludovic Courtès
2008-07-12 22:57 ` Closure? Neil Jerram
0 siblings, 2 replies; 20+ messages in thread
From: Maciek Godek @ 2008-07-11 14:48 UTC (permalink / raw
To: guile-user
Hi folks,
I've been wondering if there's any way to recall
(or get inside) an environment of a closure (= to
directly access variables bound to a closure)
And here's what I mean exactly:
(define ++ (let ((c 0)) (lambda()(set! c (1+ c))c)))
; we now have a closure
(procedure-environment ++)
;-> ((c . 0) #<eval-closure 7f6d672efa80>)
(++)
;-> 1
; Now I would like to be able to do the following:
(with (procedure-environment ++) (set! c 20))
; the c variable in closure ++ is set to 20
(define -- (with (procedure-environment ++)
(lambda()(set! c (1- c)) c)))
; here we have a new function bound to the same closure
(--)
;-> 19
Is there any way to achieve this in guile?
(it is especially important for me to make it
efficiently doable from C). I've tried some
funny combinations of eval and procedure-environment,
but they didn't work out.
Additionaly, it would be nice to see the possibility
of explicit definitions of environments, like:
(define env (make-closure (a . 1)(b . 2))
(with env (define c 3))
so that we could define the aforementioned
counter as:
(define counter-env (make-closure (c . 0)))
(define ++ (with counter-env (lambda()(set! c (1+ c))c)))
and so forth.
By the way, the let special form could then be
defined using this make-environment function
(define-macro (let bindings body)
(with (make-closure* bindings) body))
(I've suffixed the name with asterisk as some
transformations between the let bindings and
make-closure bindings are required to make it
work properly)
Hope I'm not reinventing the wheel here; it
would be nice to be able to use guile in such
way, though (and to make sure that it is
efficient from the C level as well). It
would also make scheme oop more intuitive,
as method names could be hidden inside a closure
and so it would allow the following syntax:
(with object (method args))
which corresponds to the famous dot syntax
known from many oo-languages like c++ or python:
object.method(args)
Would it be difficult to implement such
functionality in guile? (if there's none implemented yet
-- I didn't find anything in the documentation)
tia4i
(thanks in advance for implementation :)
Maciek Godek
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-11 14:48 Closure? Maciek Godek
@ 2008-07-11 15:01 ` Ludovic Courtès
2008-07-11 15:32 ` Closure? Maciek Godek
2008-07-12 22:57 ` Closure? Neil Jerram
1 sibling, 1 reply; 20+ messages in thread
From: Ludovic Courtès @ 2008-07-11 15:01 UTC (permalink / raw
To: guile-user
Hi,
"Maciek Godek" <pstrychuj@gmail.com> writes:
> I've been wondering if there's any way to recall
> (or get inside) an environment of a closure (= to
> directly access variables bound to a closure)
Yes, with `the-environment':
guile> ((lambda (a b) (the-environment)) 2 3)
(((a b) 2 3) #<eval-closure b7c6dcf8>)
But don't do that, since the representation of environments could
eventually change.
Thanks,
Ludovic.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-11 15:01 ` Closure? Ludovic Courtès
@ 2008-07-11 15:32 ` Maciek Godek
0 siblings, 0 replies; 20+ messages in thread
From: Maciek Godek @ 2008-07-11 15:32 UTC (permalink / raw
To: Ludovic Courtès; +Cc: guile-user
2008/7/11 Ludovic Courtès <ludo@gnu.org>:
> "Maciek Godek" <pstrychuj@gmail.com> writes:
>
>> I've been wondering if there's any way to recall
>> (or get inside) an environment of a closure (= to
>> directly access variables bound to a closure)
>
> Yes, with `the-environment':
>
> guile> ((lambda (a b) (the-environment)) 2 3)
> (((a b) 2 3) #<eval-closure b7c6dcf8>)
>
> But don't do that, since the representation of environments could
> eventually change.
Don't worry, I won't :)
Now I only know how to obtain environment of a closure,
but I still don't know how to get inside and modify it (I'm
lacking this `with' I wrote about).
Best regards
Maciek Godek
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
[not found] <cmu-lmtpd-26382-1215792454-10@mail-imap1.uio.no>
@ 2008-07-11 17:42 ` Kjetil S. Matheussen
2008-07-11 17:47 ` Closure? Kjetil S. Matheussen
0 siblings, 1 reply; 20+ messages in thread
From: Kjetil S. Matheussen @ 2008-07-11 17:42 UTC (permalink / raw
To: guile-user
Ludovic Court?s:
> "Maciek Godek" <pstrychuj@gmail.com> writes:
>
>> I've been wondering if there's any way to recall
>> (or get inside) an environment of a closure (= to
>> directly access variables bound to a closure)
>
> Yes, with `the-environment':
>
> guile> ((lambda (a b) (the-environment)) 2 3)
> (((a b) 2 3) #<eval-closure b7c6dcf8>)
>
> But don't do that, since the representation of environments could
> eventually change.
>
Please don't change it. :-)
It's one of those things which makes Guile special.
Maciek Godek:
>
> Don't worry, I won't :)
> Now I only know how to obtain environment of a closure,
> but I still don't know how to get inside and modify it (I'm
> lacking this `with' I wrote about).
>
The function "local-eval" is probably what you want.
I use local-eval a lot, it's one of the really really
nice features provided by Guile:
(define-macro (with env . code)
`(local-eval (quote (begin ,@code)) ,env))
(define ++
(let ((c 0))
(c-display "env" (the-environment))
(lambda()
(set! c (1+ c)) c)))
(++)
=> 1
(with (procedure-environment ++) (set! c 20))
(++)
=> 20
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-11 17:42 ` Closure? Kjetil S. Matheussen
@ 2008-07-11 17:47 ` Kjetil S. Matheussen
2008-07-11 20:54 ` Closure? Maciek Godek
2008-07-12 22:43 ` Closure? Neil Jerram
0 siblings, 2 replies; 20+ messages in thread
From: Kjetil S. Matheussen @ 2008-07-11 17:47 UTC (permalink / raw
To: Kjetil S. Matheussen; +Cc: guile-user
On Fri, 11 Jul 2008, Kjetil S. Matheussen wrote:
>
>
> Ludovic Court?s:
> > "Maciek Godek" <pstrychuj@gmail.com> writes:
> >
> >> I've been wondering if there's any way to recall
> >> (or get inside) an environment of a closure (= to
> >> directly access variables bound to a closure)
> >
> > Yes, with `the-environment':
> >
> > guile> ((lambda (a b) (the-environment)) 2 3)
> > (((a b) 2 3) #<eval-closure b7c6dcf8>)
> >
> > But don't do that, since the representation of environments could
> > eventually change.
> >
>
> Please don't change it. :-)
> It's one of those things which makes Guile special.
Sorry, I misunderstood. What I ment is that (the-environment)
etc. is what makes Guile special. I hope there is no
plans to remove all that.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-11 17:47 ` Closure? Kjetil S. Matheussen
@ 2008-07-11 20:54 ` Maciek Godek
2008-07-12 11:47 ` Closure? Kjetil S. Matheussen
2008-07-12 22:43 ` Closure? Neil Jerram
1 sibling, 1 reply; 20+ messages in thread
From: Maciek Godek @ 2008-07-11 20:54 UTC (permalink / raw
To: Kjetil S. Matheussen; +Cc: guile-user
Kjetil S. Matheussen:
> The function "local-eval" is probably what you want.
>
> I use local-eval a lot, it's one of the really really
> nice features provided by Guile:
>
>
> (define-macro (with env . code)
> `(local-eval (quote (begin ,@code)) ,env))
>
>
> (define ++
> (let ((c 0))
> (c-display "env" (the-environment))
> (lambda()
> (set! c (1+ c)) c)))
I don't actually get the line with c-display.
Does it require any additional module?
'Cause it ain' workin' for me.
(and besides what does it do?
the remaining part works fine)
> (++)
> => 1
> (with (procedure-environment ++) (set! c 20))
>
> (++)
> => 20
I'm really impressed :) Great respect!
I only wonder if there's a way to add a new
variable to an existing closure, or to delete one.
(not that I need it right now; but I imagine that
it can be implemented quite easily and efficiently
in C if the notion of closure is well-defined and
explicit)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-11 20:54 ` Closure? Maciek Godek
@ 2008-07-12 11:47 ` Kjetil S. Matheussen
2008-07-13 6:59 ` Closure? Maciek Godek
0 siblings, 1 reply; 20+ messages in thread
From: Kjetil S. Matheussen @ 2008-07-12 11:47 UTC (permalink / raw
To: Maciek Godek; +Cc: guile-user, Kjetil S. Matheussen
On Fri, 11 Jul 2008, Maciek Godek wrote:
> Kjetil S. Matheussen:
>
>> The function "local-eval" is probably what you want.
>>
>> I use local-eval a lot, it's one of the really really
>> nice features provided by Guile:
>>
>>
>> (define-macro (with env . code)
>> `(local-eval (quote (begin ,@code)) ,env))
>>
>>
>> (define ++
>> (let ((c 0))
>> (c-display "env" (the-environment))
>> (lambda()
>> (set! c (1+ c)) c)))
>
> I don't actually get the line with c-display.
> Does it require any additional module?
> 'Cause it ain' workin' for me.
> (and besides what does it do?
> the remaining part works fine)
>
Just ignore that one. c-display is just a debugging
function I forgot to remove. :-)
>> (++)
>> => 1
>> (with (procedure-environment ++) (set! c 20))
>>
>> (++)
>> => 20
>
> I'm really impressed :) Great respect!
>
> I only wonder if there's a way to add a new
> variable to an existing closure, or to delete one.
> (not that I need it right now; but I imagine that
> it can be implemented quite easily and efficiently
> in C if the notion of closure is well-defined and
> explicit)
>
Adding is easy:
(define (add-var-to-environment env name val)
(local-eval `(let ((,name ,val)) (the-environment))
env))
(define env (add-var-to-environment (the-environment) 'a 50))
(local-eval 'a env)
=> 50
I don't know if there's possible to delete a variable
in a clean way.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-11 17:47 ` Closure? Kjetil S. Matheussen
2008-07-11 20:54 ` Closure? Maciek Godek
@ 2008-07-12 22:43 ` Neil Jerram
2008-07-15 7:59 ` Closure? Ludovic Courtès
1 sibling, 1 reply; 20+ messages in thread
From: Neil Jerram @ 2008-07-12 22:43 UTC (permalink / raw
To: Kjetil S. Matheussen; +Cc: guile-user
2008/7/11 Kjetil S. Matheussen <k.s.matheussen@notam02.no>:
>
> On Fri, 11 Jul 2008, Kjetil S. Matheussen wrote:
>
>> Ludovic Court?s:
>> >
>> > guile> ((lambda (a b) (the-environment)) 2 3)
>> > (((a b) 2 3) #<eval-closure b7c6dcf8>)
>> >
>> > But don't do that, since the representation of environments could
>> > eventually change.
> Sorry, I misunderstood. What I ment is that (the-environment)
> etc. is what makes Guile special. I hope there is no
> plans to remove all that.
I agree. I can't see a reason why we might want to remove
`the-environment' and `local-eval', even if Guile's internal
representation of environments changes, because the ideas that there
_is_ a lexical environment inside a lambda (or a let ...), and that
one can evaluate with respect to these environments, are absolutely
fundamental in Scheme.
On the other hand, it would be unsafe (w.r.t. the future) to write
code that depends on the current representation. If you find yourself
doing that, better to ask for an official abstraction of whatever
you're trying to do. (And even better to provide a patch for it!)
For example, I see no problem with Kjetil's add-var-to-environment,
because it doesn't depend on the representation.
Neil
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-11 14:48 Closure? Maciek Godek
2008-07-11 15:01 ` Closure? Ludovic Courtès
@ 2008-07-12 22:57 ` Neil Jerram
2008-07-13 6:57 ` Closure? Maciek Godek
1 sibling, 1 reply; 20+ messages in thread
From: Neil Jerram @ 2008-07-12 22:57 UTC (permalink / raw
To: Maciek Godek; +Cc: guile-user
Hi Maciek,
Just picking up another point from your original email. You may have
already worked this out, but just in case...
2008/7/11 Maciek Godek <pstrychuj@gmail.com>:
>
> Additionaly, it would be nice to see the possibility
> of explicit definitions of environments, like:
>
> (define env (make-closure (a . 1)(b . 2))
> (with env (define c 3))
This is equivalent to:
(define env (let ((a 1) (b 2)) (the-environment)))
(local-eval '(define c 3) env)
except that the last line fails with a "Bad define placement" error.
That's because there are special rules for defines inside lexical
scopes.
> so that we could define the aforementioned
> counter as:
> (define counter-env (make-closure (c . 0)))
> (define ++ (with counter-env (lambda()(set! c (1+ c))c)))
This one really works:
(define counter-env (let ((c 0)) (the-environment)))
(define (++) (local-eval '(begin (set! c (+ c 1)) c) counter-env))
So, in summary, make-closure wouldn't provide anything more than what
we already have.
Regards,
Neil
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-12 22:57 ` Closure? Neil Jerram
@ 2008-07-13 6:57 ` Maciek Godek
2008-07-13 22:56 ` Closure? Neil Jerram
0 siblings, 1 reply; 20+ messages in thread
From: Maciek Godek @ 2008-07-13 6:57 UTC (permalink / raw
To: Neil Jerram; +Cc: guile-user
> Hi Maciek,
>
> Just picking up another point from your original email. You may have
> already worked this out, but just in case...
OK, it's always good to say to much than not to say enough
(at least if it comes to hacking ;])
>> Additionaly, it would be nice to see the possibility
>> of explicit definitions of environments, like:
>>
>> (define env (make-closure (a . 1)(b . 2))
>> (with env (define c 3))
>
> This is equivalent to:
> (define env (let ((a 1) (b 2)) (the-environment)))
> (local-eval '(define c 3) env)
>
> except that the last line fails with a "Bad define placement" error.
> That's because there are special rules for defines inside lexical
> scopes.
As the practise shows, although guile documentation says something
different. In section 3.1.4.7 (A Shared Persistent Variable)
"An important detail here is that the `get-balance' and `deposit'
variables must be set up by `define'ing them at top level and then
`set!'ing their values inside the `let' body. Using `define' within
the `let' body would not work: this would create variable bindings
within the local `let' environment that would not be accessible at top
level."
So one might conclude that it _is_ possible to use define inside
a 'let' form.
>> so that we could define the aforementioned
>> counter as:
>> (define counter-env (make-closure (c . 0)))
>> (define ++ (with counter-env (lambda()(set! c (1+ c))c)))
>
> This one really works:
>
> (define counter-env (let ((c 0)) (the-environment)))
> (define (++) (local-eval '(begin (set! c (+ c 1)) c) counter-env))
>
> So, in summary, make-closure wouldn't provide anything more than what
> we already have.
Yes, since there's local-eval and the-environment, everything I've
ever dreamed of is possible :)
But as I've concluded from the discourse, neither of these is
defined in R5RS (and it makes me wonder)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-12 11:47 ` Closure? Kjetil S. Matheussen
@ 2008-07-13 6:59 ` Maciek Godek
0 siblings, 0 replies; 20+ messages in thread
From: Maciek Godek @ 2008-07-13 6:59 UTC (permalink / raw
To: guile-user
>> I don't actually get the line with c-display.
>> Does it require any additional module?
>> 'Cause it ain' workin' for me.
>> (and besides what does it do?
>> the remaining part works fine)
>>
>
> Just ignore that one. c-display is just a debugging
> function I forgot to remove. :-)
OK, actually that's exactly what I did :D
>>> (++)
>>> => 1
>>> (with (procedure-environment ++) (set! c 20))
>>>
>>> (++)
>>> => 20
>>
>> I'm really impressed :) Great respect!
>>
>> I only wonder if there's a way to add a new
>> variable to an existing closure, or to delete one.
>> (not that I need it right now; but I imagine that
>> it can be implemented quite easily and efficiently
>> in C if the notion of closure is well-defined and
>> explicit)
>>
>
> Adding is easy:
>
> (define (add-var-to-environment env name val)
> (local-eval `(let ((,name ,val)) (the-environment))
> env))
>
> (define env (add-var-to-environment (the-environment) 'a 50))
>
> (local-eval 'a env)
> => 50
Bravo! Gee, thanks a lot!
> I don't know if there's possible to delete a variable
> in a clean way.
That's nothing urgent. The thing is that I started to
perceive the global environment as a special closure
(I think it could be a nice generalization) and in that
model, the "define" and "undefine" forms could
operate within the nearest scope, which could make
the following constructs legal:
(with env (define x 20))
(with env (undefine x))
That would be freakin' cool.
I imagine that there could be a way to redefine
"define" inside a closure env (and perhaps
to invent an non-portable solution to undefine),
but I'd prefer to leave this idea to the ones who
implement guile's internals, so that there's only
one "define" operating on different scopes.
(Or maybe it only seems so simple...)
Thanks once again for showing me the power of
local-eval, for it's so close to what I've been looking
for.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-13 6:57 ` Closure? Maciek Godek
@ 2008-07-13 22:56 ` Neil Jerram
2008-07-14 1:15 ` Closure? Maciek Godek
0 siblings, 1 reply; 20+ messages in thread
From: Neil Jerram @ 2008-07-13 22:56 UTC (permalink / raw
To: Maciek Godek; +Cc: guile-user
2008/7/13 Maciek Godek <pstrychuj@gmail.com>:
>> except that the last line fails with a "Bad define placement" error.
>> That's because there are special rules for defines inside lexical
>> scopes.
>
> As the practise shows, although guile documentation says something
> different. In section 3.1.4.7 (A Shared Persistent Variable)
>
> "An important detail here is that the `get-balance' and `deposit'
> variables must be set up by `define'ing them at top level and then
> `set!'ing their values inside the `let' body. Using `define' within
> the `let' body would not work: this would create variable bindings
> within the local `let' environment that would not be accessible at top
> level."
>
> So one might conclude that it _is_ possible to use define inside
> a 'let' form.
Which would be correct! For example:
(let ((a 1))
(define b 2)
(+ a b))
=>
3
Whereas:
(let ((a 1))
(display a)
(newline)
(define b 2)
(+ a b))
=>
ERROR: Bad define placement
The "special rules" are just that any defines have to come before
anything else in the body of the let.
I don't know exactly how it works out that using a define in
local-eval falls foul of the define placement rule, but it is not hard
to imagine that it could do.
> Yes, since there's local-eval and the-environment, everything I've
> ever dreamed of is possible :)
> But as I've concluded from the discourse, neither of these is
> defined in R5RS (and it makes me wonder)
Well I've never thought this through before, but perhaps that is
because in many cases it is equivalent to create a lambda at the point
where you would call the-environment, containing the code that you
would later pass to local-eval.
For example, the ++ example then becomes:
(define ++ (let ((c 0)) (lambda () (begin (set! c (+ c 1)) c))))
- which is the traditional way of writing this example.
Regards,
Neil
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-13 22:56 ` Closure? Neil Jerram
@ 2008-07-14 1:15 ` Maciek Godek
0 siblings, 0 replies; 20+ messages in thread
From: Maciek Godek @ 2008-07-14 1:15 UTC (permalink / raw
To: Neil Jerram; +Cc: guile-user
>> As the practise shows, although guile documentation says something
>> different. In section 3.1.4.7 (A Shared Persistent Variable)
>>
>> "An important detail here is that the `get-balance' and `deposit'
>> variables must be set up by `define'ing them at top level and then
>> `set!'ing their values inside the `let' body. Using `define' within
>> the `let' body would not work: this would create variable bindings
>> within the local `let' environment that would not be accessible at top
>> level."
>>
>> So one might conclude that it _is_ possible to use define inside
>> a 'let' form.
>
> Which would be correct! For example:
>
> (let ((a 1))
> (define b 2)
> (+ a b))
> =>
> 3
>
> Whereas:
>
> (let ((a 1))
> (display a)
> (newline)
> (define b 2)
> (+ a b))
> =>
> ERROR: Bad define placement
>
> The "special rules" are just that any defines have to come before
> anything else in the body of the let.
Yeah, guess you're right (under certain circumstances :P)
> I don't know exactly how it works out that using a define in
> local-eval falls foul of the define placement rule, but it is not hard
> to imagine that it could do.
The other question is: is it really necessary to impose such
limitations on "define". Why is it required to make its position
inside let privileged?
>> Yes, since there's local-eval and the-environment, everything I've
>> ever dreamed of is possible :)
>> But as I've concluded from the discourse, neither of these is
>> defined in R5RS (and it makes me wonder)
>
> Well I've never thought this through before, but perhaps that is
> because in many cases it is equivalent to create a lambda at the point
> where you would call the-environment, containing the code that you
> would later pass to local-eval.
>
> For example, the ++ example then becomes:
>
> (define ++ (let ((c 0)) (lambda () (begin (set! c (+ c 1)) c))))
>
> - which is the traditional way of writing this example.
You didn't focus :>
The whole idea of accessing a closure environment
was in fact to make scheme object oriented
programming more intuitive.
In guile info pages there's an oo closure example:
(section 3.1.4.9 "Example 4: Object Orientation")
"
(define (make-account)
(let ((balance 0))
(define (get-balance)
balance)
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(define (withdraw amount)
(deposit (- amount)))
(lambda args
(apply
(case (car args)
((get-balance) get-balance)
((deposit) deposit)
((withdraw) withdraw)
(else (error "Invalid method!")))
(cdr args)))))
(define my-account (make-account))
"
Notice the ugly "case" statement that requires
the variables to be accessed in the following manner
(the same example, a few lines later):
"
(my-account 'get-balance)
=>
0
(my-account 'withdraw 5)
=>
-5
(my-account 'deposit 396)
=>
391
(my-account 'get-balance)
=>
391
"
This is ugly as it requires doubling the names of functions.
Perhaps it could be overcome with some sort of macro,
but the "with" I proposed allows to avoid the whole "case"
and to write (after slight modifications in the "let" form):
(with my-account (get-balance))
Or maybe I think wrong; I'm new in the world of lisp,
so please forgive me my mistakes :)
Best regards,
M
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
[not found] <cmu-lmtpd-15105-1216051603-3@mail-imap1.uio.no>
@ 2008-07-14 16:30 ` Kjetil S. Matheussen
2008-07-14 21:14 ` Closure? Maciek Godek
[not found] ` <e2ceda030807141414i5acef7d1h37d12d14e01cc1d@mail.gmail.com>
0 siblings, 2 replies; 20+ messages in thread
From: Kjetil S. Matheussen @ 2008-07-14 16:30 UTC (permalink / raw
To: guile-user
"Maciek Godek"
>> I don't know exactly how it works out that using a define in
>> local-eval falls foul of the define placement rule, but it is not hard
>> to imagine that it could do.
>
> The other question is: is it really necessary to impose such
> limitations on "define". Why is it required to make its position
> inside let privileged?
>
The scheme standard is a bit pedantic. The above I would write
like this:
(let ()
(define a)
(display a)
(newline)
(let ()
(define b 2)
(+ a b)))
>>> Yes, since there's local-eval and the-environment, everything I've
>>> ever dreamed of is possible :)
>>> But as I've concluded from the discourse, neither of these is
>>> defined in R5RS (and it makes me wonder)
>>
>> Well I've never thought this through before, but perhaps that is
>> because in many cases it is equivalent to create a lambda at the point
>> where you would call the-environment, containing the code that you
>> would later pass to local-eval.
>>
>> For example, the ++ example then becomes:
>>
>> (define ++ (let ((c 0)) (lambda () (begin (set! c (+ c 1)) c))))
>>
>> - which is the traditional way of writing this example.
>
> You didn't focus :>
> The whole idea of accessing a closure environment
> was in fact to make scheme object oriented
> programming more intuitive.
>
> In guile info pages there's an oo closure example:
>
> (section 3.1.4.9 "Example 4: Object Orientation")
> "
> (define (make-account)
> (let ((balance 0))
> (define (get-balance)
> balance)
> (define (deposit amount)
> (set! balance (+ balance amount))
> balance)
> (define (withdraw amount)
> (deposit (- amount)))
>
> (lambda args
> (apply
> (case (car args)
> ((get-balance) get-balance)
> ((deposit) deposit)
> ((withdraw) withdraw)
> (else (error "Invalid method!")))
> (cdr args)))))
>
> (define my-account (make-account))
> "
> Notice the ugly "case" statement that requires
> the variables to be accessed in the following manner
> (the same example, a few lines later):
> "
> (my-account 'get-balance)
> =>
> 0
>
> (my-account 'withdraw 5)
> =>
> -5
>
> (my-account 'deposit 396)
> =>
> 391
>
> (my-account 'get-balance)
> =>
> 391
> "
>
> This is ugly as it requires doubling the names of functions.
> Perhaps it could be overcome with some sort of macro,
> but the "with" I proposed allows to avoid the whole "case"
> and to write (after slight modifications in the "let" form):
>
> (with my-account (get-balance))
>
> Or maybe I think wrong; I'm new in the world of lisp,
> so please forgive me my mistakes :)
>
I think local-eval is necessary for making
a namespace system of the below type without having to use
codewalking macros to expand the bodies of functions:
(define-namespace bank)
(def-bank sum 0)
(def-bank (add n)
(set! sum (+ n sum)) ;; Note that it's enough to write "sum".
(bank.add 50)
bank.sum
=> 50
But for implementing a message passing OO system,
it's easier to use macros and hash tables, plus
that it probably performs much better:
(def-class <bank>
(def-var sum 0)
(def-method (add n)
(set! sum (+ n sum)))
(define bank (new <bank>))
(-> bank add 50)
(-> bank sum)
=> 50
There's a bunch of these systems for scheme.
The syntax above is used from
http://snd.cvs.sourceforge.net/snd/cvs-snd/oo.scm?view=log
Guile's own OO system called GOOPS is also very nice.
GOOPS a quite verbose but very powerful and a lot
more interactive. It's similar to CL's CLOS, which
you should look at if you are not familiar with
already.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-14 16:30 ` Closure? Kjetil S. Matheussen
@ 2008-07-14 21:14 ` Maciek Godek
[not found] ` <e2ceda030807141414i5acef7d1h37d12d14e01cc1d@mail.gmail.com>
1 sibling, 0 replies; 20+ messages in thread
From: Maciek Godek @ 2008-07-14 21:14 UTC (permalink / raw
To: guile-user
Kjetil S. Matheussen:
> I think local-eval is necessary for making
> a namespace system of the below type without having to use
> codewalking macros to expand the bodies of functions:
>
> (define-namespace bank)
> (def-bank sum 0)
> (def-bank (add n)
> (set! sum (+ n sum)) ;; Note that it's enough to write "sum".
>
> (bank.add 50)
> bank.sum
> => 50
This certainly looks like trashing global namespace
(which isn't good in the long run) and doesn't allow you
to have object handlers (like many variables referring
to the same object) without additional quirks.
> But for implementing a message passing OO system,
> it's easier to use macros and hash tables, plus
> that it probably performs much better:
Pefrorms better than local-eval or better than
define-namespace?
> (def-class <bank>
> (def-var sum 0)
> (def-method (add n)
> (set! sum (+ n sum)))
>
> (define bank (new <bank>))
> (-> bank add 50)
> (-> bank sum)
> => 50
>
> There's a bunch of these systems for scheme.
> The syntax above is used from
> http://snd.cvs.sourceforge.net/snd/cvs-snd/oo.scm?view=log
>
> Guile's own OO system called GOOPS is also very nice.
> GOOPS a quite verbose but very powerful and a lot
> more interactive. It's similar to CL's CLOS, which
> you should look at if you are not familiar with
> already.
Well, I've read some documentation of GOOPS and then
I took a glimpse at its source. It has at least a few
disadvantages, for it is an object system implemented in
scheme -- it is therefore hard to access its objects from C
(while closures are easily accessible through
scm_local_eval) and it probably won't run as fast as
local-eval, at least conceptually.
(It is essential to observe that closures are already like
objects and that any additional object systems are doubtfully
needed with closures implemented properly -- as long as
you can access their scope)
Yet still I reserve the right to be wrong as a newbie :)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
[not found] ` <e2ceda030807141414i5acef7d1h37d12d14e01cc1d@mail.gmail.com>
@ 2008-07-14 21:41 ` Kjetil S. Matheussen
2008-07-14 22:46 ` Closure? Maciek Godek
0 siblings, 1 reply; 20+ messages in thread
From: Kjetil S. Matheussen @ 2008-07-14 21:41 UTC (permalink / raw
To: Maciek Godek, guile-user
On Mon, 14 Jul 2008, Maciek Godek wrote:
> Kjetil S. Matheussen:
>
>> I think local-eval is necessary for making
>> a namespace system of the below type without having to use
>> codewalking macros to expand the bodies of functions:
>>
>> (define-namespace bank)
>> (def-bank sum 0)
>> (def-bank (add n)
>> (set! sum (+ n sum)) ;; Note that it's enough to write "sum".
>>
>> (bank.add 50)
>> bank.sum
>> => 50
>
> This certainly looks like trashing global namespace
> (which isn't good in the long run) and doesn't allow you
> to have object handlers (like many variables referring
> to the same object) without additional quirks.
>
It's just an example of what local-eval can
be good for. It's an example of a _namespace
system_, and not an _OO system_.
>> But for implementing a message passing OO system,
>> it's easier to use macros and hash tables, plus
>> that it probably performs much better:
>
> Pefrorms better than local-eval or better than
> define-namespace?
>
It's much faster than local-eval since it's not
calling eval for each method call. The overhead of calling
a method in an OO system, based on hash tables
and macros, is basically just the time it takes looking
up the address of the function from
a hash-table. For guile, which has a large
overhead already, your program probably won't
go any slower doing this compared to calling
functions directly:
(define (make-bank sum)
(let ((attributes (make-hash-table)))
(define dispatcher
(lambda (which . rest)
(apply (hashq-ref attributes which) rest)))
(hashq-set! attributes 'sum (lambda ()
sum))
(hashq-set! attributes 'add (lambda (n)
(set! sum (+ n sum))))
dispatcher))
(define bank (make-bank 0))
(bank 'add 2)
(bank 'sum)
=> 2
And by using some macros, you can make the syntax look
much prettier.
>> (def-class <bank>
>> (def-var sum 0)
>> (def-method (add n)
>> (set! sum (+ n sum)))
>>
>> (define bank (new <bank>))
>> (-> bank add 50)
>> (-> bank sum)
>> => 50
>>
>> There's a bunch of these systems for scheme.
>> The syntax above is used from
>> http://snd.cvs.sourceforge.net/snd/cvs-snd/oo.scm?view=log
>>
>> Guile's own OO system called GOOPS is also very nice.
>> GOOPS a quite verbose but very powerful and a lot
>> more interactive. It's similar to CL's CLOS, which
>> you should look at if you are not familiar with
>> already.
>
> Well, I've read some documentation of GOOPS and then
> I took a glimpse at its source. It has at least a few
> disadvantages, for it is an object system implemented in
> scheme -- it is therefore hard to access its objects from C
> (while closures are easily accessible through
> scm_local_eval) and it probably won't run as fast as
> local-eval, at least conceptually.
>
local-eval is forced to interpret its argument, and is therefore, at least
conceptually, very slow. I very much doubt GOOPS is that slow,
but I don't know how GOOPS is implemented though. :-)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-14 21:41 ` Closure? Kjetil S. Matheussen
@ 2008-07-14 22:46 ` Maciek Godek
0 siblings, 0 replies; 20+ messages in thread
From: Maciek Godek @ 2008-07-14 22:46 UTC (permalink / raw
To: Kjetil S. Matheussen; +Cc: guile-user
>> This certainly looks like trashing global namespace
>> (which isn't good in the long run) and doesn't allow you
>> to have object handlers (like many variables referring
>> to the same object) without additional quirks.
>>
>
> It's just an example of what local-eval can
> be good for. It's an example of a _namespace
> system_, and not an _OO system_.
OK, sorry, I didn't understand at first.
>>> But for implementing a message passing OO system,
>>> it's easier to use macros and hash tables, plus
>>> that it probably performs much better:
>>
>> Pefrorms better than local-eval or better than
>> define-namespace?
>>
>
> It's much faster than local-eval since it's not
> calling eval for each method call. The overhead of calling
> a method in an OO system, based on hash tables
> and macros, is basically just the time it takes looking
> up the address of the function from
> a hash-table. For guile, which has a large
> overhead already, your program probably won't
> go any slower doing this compared to calling
> functions directly:
Is local-eval much slower than the usual eval?
It is very probable that I get something wrong
right now, but according to my imagination of
a possible implementation :D of eval it could be
the same function (and the closure would
actually be implemented as a hash table or
something like that).
And eval is actually what lisp is doing all the
time, so the only thing that changes here
is the scope (=reference to a hash table)
That's how, I believe, it could be implemented.
(but I've never tried to do that nor read it in the code,
so I'm just being a smart ass)
> (define (make-bank sum)
> (let ((attributes (make-hash-table)))
> (define dispatcher
> (lambda (which . rest)
> (apply (hashq-ref attributes which) rest)))
> (hashq-set! attributes 'sum (lambda ()
> sum))
> (hashq-set! attributes 'add (lambda (n)
> (set! sum (+ n sum))))
> dispatcher))
>
> (define bank (make-bank 0))
> (bank 'add 2)
> (bank 'sum)
> => 2
>
>
> And by using some macros, you can make the syntax look
> much prettier.
To me it already looks very pretty (and works just
as efficiently as my imagination of local-eval operation :>).
This might be just what I've been looking for.
Thanks a lot.
>> Well, I've read some documentation of GOOPS and then
>> I took a glimpse at its source. It has at least a few
>> disadvantages, for it is an object system implemented in
>> scheme -- it is therefore hard to access its objects from C
>> (while closures are easily accessible through
>> scm_local_eval) and it probably won't run as fast as
>> local-eval, at least conceptually.
>>
>
> local-eval is forced to interpret its argument, and is therefore, at least
> conceptually, very slow. I very much doubt GOOPS is that slow,
> but I don't know how GOOPS is implemented though. :-)
Neither do I (I just saw a lot of code, many many layers of code
so I've concluded that it is a complex multi-layer system I don't
want to get into). I never realized that eval causes such terrible
overheads ((procedure-source eval) doesn't show any of the
mysteries of the universe :P)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-12 22:43 ` Closure? Neil Jerram
@ 2008-07-15 7:59 ` Ludovic Courtès
2008-07-15 9:11 ` Closure? Andy Wingo
0 siblings, 1 reply; 20+ messages in thread
From: Ludovic Courtès @ 2008-07-15 7:59 UTC (permalink / raw
To: guile-user
Hello,
"Neil Jerram" <neiljerram@googlemail.com> writes:
> 2008/7/11 Kjetil S. Matheussen <k.s.matheussen@notam02.no>:
>>
>> On Fri, 11 Jul 2008, Kjetil S. Matheussen wrote:
>>
>>> Ludovic Court?s:
>>> >
>>> > guile> ((lambda (a b) (the-environment)) 2 3)
>>> > (((a b) 2 3) #<eval-closure b7c6dcf8>)
>>> >
>>> > But don't do that, since the representation of environments could
>>> > eventually change.
>
>> Sorry, I misunderstood. What I ment is that (the-environment)
>> etc. is what makes Guile special. I hope there is no
>> plans to remove all that.
>
> I agree. I can't see a reason why we might want to remove
> `the-environment' and `local-eval', even if Guile's internal
> representation of environments changes, because the ideas that there
> _is_ a lexical environment inside a lambda (or a let ...), and that
> one can evaluate with respect to these environments, are absolutely
> fundamental in Scheme.
Agreed. I just said the *representation* of closures could change.
(Besides, keep in mind that `the-environment' et al. aren't
documented...)
> On the other hand, it would be unsafe (w.r.t. the future) to write
> code that depends on the current representation. If you find yourself
> doing that, better to ask for an official abstraction of whatever
> you're trying to do. (And even better to provide a patch for it!)
Agreed.
Thanks,
Ludovic.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-15 7:59 ` Closure? Ludovic Courtès
@ 2008-07-15 9:11 ` Andy Wingo
2008-07-16 16:42 ` Closure? Ludovic Courtès
0 siblings, 1 reply; 20+ messages in thread
From: Andy Wingo @ 2008-07-15 9:11 UTC (permalink / raw
To: Ludovic Courtès; +Cc: guile-user
On Tue 15 Jul 2008 09:59, ludo@gnu.org (Ludovic Courtès) writes:
> "Neil Jerram" <neiljerram@googlemail.com> writes:
>
>> I agree. I can't see a reason why we might want to remove
>> `the-environment' and `local-eval', even if Guile's internal
>> representation of environments changes, because the ideas that there
>> _is_ a lexical environment inside a lambda (or a let ...), and that
>> one can evaluate with respect to these environments, are absolutely
>> fundamental in Scheme.
>
> Agreed. I just said the *representation* of closures could change.
You can determine exactly what should be in the lexical environments at
compile-time; thus you can allocate a fixed structure of variables to
hold the lexicals. I suppose that in theory, you might be able to
implement an augmentable `the-environment' in guile-vm, but I do not
plan on doing so myself.
Or maybe so, I do have some old code I want to get back to at some
point... http://ambient.2y.net/soundscrape/docs/tutorial/a-few-notes-on-music/a-new-vocabulary/
Andy
--
http://wingolog.org/
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Closure?
2008-07-15 9:11 ` Closure? Andy Wingo
@ 2008-07-16 16:42 ` Ludovic Courtès
0 siblings, 0 replies; 20+ messages in thread
From: Ludovic Courtès @ 2008-07-16 16:42 UTC (permalink / raw
To: guile-user
Hey,
Andy Wingo <wingo@pobox.com> writes:
> You can determine exactly what should be in the lexical environments at
> compile-time; thus you can allocate a fixed structure of variables to
> hold the lexicals.
In addition, one expects constant-time access to local variables
(aka. "ilocs"), which the current list-based closure representation
fails to achieve (see `scm_ilookup ()').
Thanks,
Ludovic.
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2008-07-16 16:42 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-11 14:48 Closure? Maciek Godek
2008-07-11 15:01 ` Closure? Ludovic Courtès
2008-07-11 15:32 ` Closure? Maciek Godek
2008-07-12 22:57 ` Closure? Neil Jerram
2008-07-13 6:57 ` Closure? Maciek Godek
2008-07-13 22:56 ` Closure? Neil Jerram
2008-07-14 1:15 ` Closure? Maciek Godek
[not found] <cmu-lmtpd-26382-1215792454-10@mail-imap1.uio.no>
2008-07-11 17:42 ` Closure? Kjetil S. Matheussen
2008-07-11 17:47 ` Closure? Kjetil S. Matheussen
2008-07-11 20:54 ` Closure? Maciek Godek
2008-07-12 11:47 ` Closure? Kjetil S. Matheussen
2008-07-13 6:59 ` Closure? Maciek Godek
2008-07-12 22:43 ` Closure? Neil Jerram
2008-07-15 7:59 ` Closure? Ludovic Courtès
2008-07-15 9:11 ` Closure? Andy Wingo
2008-07-16 16:42 ` Closure? Ludovic Courtès
[not found] <cmu-lmtpd-15105-1216051603-3@mail-imap1.uio.no>
2008-07-14 16:30 ` Closure? Kjetil S. Matheussen
2008-07-14 21:14 ` Closure? Maciek Godek
[not found] ` <e2ceda030807141414i5acef7d1h37d12d14e01cc1d@mail.gmail.com>
2008-07-14 21:41 ` Closure? Kjetil S. Matheussen
2008-07-14 22:46 ` Closure? Maciek Godek
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).