[gambit-list] Two basic questions regarding the use of make-parameter and parameterize.

Patrick Li 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:
   (define (make-random-stack)
      (let [(mystack (make-stack))]
         (push mystack 0)
         (push mystack 1)
         (store-random-callback
            (lambda () (push mystack 0)))
         (helper mystack)
         mystack))

   (define (helper s)
      (pop 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.

   (define (make-random-stack)
      (let [(mystack (make-stack))]
         (with-stack mystack
            (push 0)
            (push 1)
            (store-random-callback
               (lambda () (push 0)))
            (helper))
         mystack))

   (define (helper)
      (pop))


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



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
> correct?
>
> 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))
>     (f)))
>
> (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.
>
> Marc
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.iro.umontreal.ca/pipermail/gambit-list/attachments/20131104/dbb86052/attachment.htm>


More information about the Gambit-list mailing list