[gambit-list] Two basic questions regarding the use of make-parameter and parameterize.
patrickli.2001 at gmail.com
Mon Nov 4 12:41:31 EST 2013
> The closure in g remembers that p was bound to 222, and the closure in
> h remembers that p was bound to 111. So in the call (g 0) the result
> will be 222 (i.e. 0+222), and in the first call (h 0) the result will
> be 111 (i.e. 0+111). What happens in the last expression? The
> parameter p is dynamically bound to 333, and then h is called. Because
> h remembers that p was bound to 111, it reinstates this binding for
> the evaluation of its body, (+ y (p)), so the result is 111
> (i.e. 0+111). So that's what I meant when I said "a calling function
> can't use parameter objects to pass implicit parameters to the called
> function". The idiom (parameterize ((p ...)) (fn ...)) no longer
> works for passing the implicit parameter p to fn.
> Note that when parameter objects are automatically captured by
> closures, parameter objects behave like lexical variables!
> Consequently they would be completely redundant in the language.
I understand now. Thank you for the in-depth explanation. Okay, I
realize that I am asking for something slightly inconsistent. Do you
have any ideas then how I could design a library interface to
accomplish the following?
EXPLICIT PARAMETER PASSING:
Consider a library for managing stacks.
(make-stack) : Create a new stack
(push stack item) : Pushes the item onto the stack.
(peek stack) : Retrieves the top of the stack.
(pop stack) : Pops off the top of the stack.
(empty? stack) : Checks if the stack is empty.
Usage of this library would be standard:
(let [(mystack (make-stack))]
(push mystack 0)
(push mystack 1)
(lambda () (push mystack 0)))
(define (helper s)
IMPLICIT PARAMETER PASSING:
It is annoying to have to thread the stack variable through the
argument lists of all my functions. So I would rather have it passed
implicitly. The (with-stack a-stack ...) command/macro tells the stack
library that I would like all following push/pop/peek commands to
operate on the given a-stack.
(let [(mystack (make-stack))]
(lambda () (push 0)))
I feel that this is something reasonable to ask for, even though I now
know that there is some inherent ambiguity (related to when and when
not to capture) in the interface.
Thank you again for the in-depth explanations.
On Mon, Nov 4, 2013 at 7:46 AM, Marc Feeley <feeley at iro.umontreal.ca> wrote:
> On Nov 3, 2013, at 2:18 PM, Patrick Li <patrickli.2001 at gmail.com> wrote:
> > Thank you very much for your reply Marc!
> > >This implementation technique has problems:
> > >
> > >- the cost of call/cc will be proportional to the number of parameters
> > > (because at the time of creating a continuation you have to take a
> > > snapshot of all those parameters, and when the continuation is
> > > invoked you have to copy the snapshot back to the parameters)
> > The cost of call/cc would be proportional to the number of
> > parameterize (and hence dynamic-wind) calls you would be currently
> > nested in right? Other parts of the program, that are not as deeply
> > nested in parameterize, would not be affected. Is my understanding
> When a continuation is invoked, some dynamic bindings must first be undone
> (unwinding phase) and then some dynamic bindings myst be redone (winding
> phase). So it is proportional to the sum of the nesting depths (or more
> precisely, the nesting depth from the closest common branches of the
> dynamic environment).
> > >- it doesn't handle mutation (you would have to introduce cells for
> > > that, and dereference these cells when accessing the parameter)
> > I don't quite understand this aspect.
> > I expect:
> > (parameterize [(a 3)]
> > ... body ...)
> > to be roughly equivalent to:
> > (let [(old-a a)]
> > (dynamic-wind
> > (lambda () (set! a 3))
> > (lambda () ... body ...)
> > (lambda () (set! a old-a))))
> > So I would expect to be able to use (set! a 10) from within the body
> > just like normal.
> This would work in a single threaded Scheme implementation, but not in a
> multithreaded implementation. For your information this is called the
> "shallow binding" implementation, and it was (is) used in some
> implementations of Lisp (google it).
> > >- it doesn't work in a multithreaded environment (the content of the
> > > parameters will be clobbered by the different threads operating on
> > > those parameters)
> > Yes. Threading & Dynamic bindings together seem subtle.
> > >> 2) Is there a way to get a closure to close over the dynamic
> > >> environment? The use case is that I want to use dynamic variables
> > >> *purely* to avoid having to pass commonly used arguments
> > >> explicitly. Thus I am using the dynamic variable as an "implicitly"
> > >> passed argument. And in such a case, I would like closures to refer
> > >> to the value of the dynamic variable at the time of closure
> > >> creation.
> > >
> > >Closures capture the lexical variables. Parameter objects can't be
> > >automatically captured by closures because that would mean that a
> > >calling function can't use parameter objects to pass implicit
> > >parameters to the called function. So if you want to capture the
> > >value of some parameter objects you will have to do it manually,
> > >i.e. something like:
> > >
> > >(define p (make-parameter 42))
> > >
> > >(define (make-adder) ;; creates a function which adds the value of
> > > parameter p (let ((captured-p (p))) (lambda (x) (parameterize ((p
> > > captured-p)) (+ x (p))))))
> > >
> > >(define a (make-adder))
> > >
> > >(p 1000) ;; change value of parameter p
> > >
> > >(pp (a 10)) => 52
> > >
> > >(p) => 1000
> > I understand the example that you gave on how to manually capture the
> > parameter object. Could you clarify what you mean by "a
> > calling function can't use parameter objects to pass implicit
> > parameters to the called function"?
> Lets assume that parameter objects are automatically captured by closures.
> That means that when a closure is called, the dynamic bindings that were
> in effect at the moment that the closure was created will be reinstated at
> the beginning of the execution of the body of the closure. Now examine the
> following code and ponder what is returned by the last 3 expressions:
> (define p (make-parameter 111))
> (define (make-adder x)
> (parameterize ((p x))
> (define (f)
> (lambda (y) (+ y (p))))
> (define g (make-adder 222))
> (define h (f))
> (g 0) => ?
> (h 0) => ?
> (parameterize ((p 333)) (h 0)) => ?
> The closure in g remembers that p was bound to 222, and the closure in h
> remembers that p was bound to 111. So in the call (g 0) the result will be
> 222 (i.e. 0+222), and in the first call (h 0) the result will be 111 (i.e.
> 0+111). What happens in the last expression? The parameter p is
> dynamically bound to 333, and then h is called. Because h remembers that p
> was bound to 111, it reinstates this binding for the evaluation of its
> body, (+ y (p)), so the result is 111 (i.e. 0+111). So that's what I meant
> when I said "a calling function can't use parameter objects to pass
> implicit parameters to the called function". The idiom (parameterize ((p
> ...)) (fn ...)) no longer works for passing the implicit parameter p to fn.
> Note that when parameter objects are automatically captured by closures,
> parameter objects behave like lexical variables! Consequently they would
> be completely redundant in the language.
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Gambit-list