This is the mail archive of the guile@cygnus.com mailing list for the guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: "Current" solution for generalized set!




I think I understand now. Thanks for being patient with me. Your prime goal
was to associate a getter
function with a setter function. My ideas were about making sure that
different classes of object could
have setters with the same name. So I was concerned about dispatching over
the class of object.
Whereas your solution is about dispatching according to an actual instance
of a procedure. I guess
they are complementary concepts.

It does suggest to me a more general mechanism for what you want to do
though. What you want is to use
the one function name, like "car" as the name of a getter and setter,
depending on the usage. It suggests
to me that you actually want to dispatch similar to CLOS except not on
type, but on identity.

Wouldn't it be cool if the CLOS system were expanded to encompass this
idea?
Generic functions are normally declared (define-generic name ((arg1
<arg1type>) ...))
What if instead of a type you could have a symbol? Like this..
(define-generic setter ((proc 'car)) set-car! )

Then when you call (setter car) it would call the above generic function
returning set-car!. Thus
(set! (car foo) v)
becomes..
((setter 'car ) v foo)
becomes..
(set-car! v foo)

Just looking for a way to make a specific solution more general...
Depending on how CLOS is
implemented, I can imagine this might "just work" if CLOS doesn't insist
that the thing dispatched
on is a class.

Then you could also have a generic initializer...
(define-generic initializer ((proc 'cdr)) (lambda (obj) (set-cdr! obj
'())))
and perhaps
(init! (cdr foo))
becomes..
((initializer 'cdr) foo)
becomes..
((lambda (obj) (set-cdr! obj '()))  foo)
becomes..
(set-cdr! foo '())




Chris.Bitmead@misys.com.au writes:

> dispatch_table[scm->type]->display(scm);
>
> I can't see that it could be inlined beyond this because we don't know
the > run-time type.

Let's say that we're compiling module B which imports bindings
read-only from module A, among them the variable x.

In our program we have the expression

  (display x)

Let's now assume that x is a free variable, i.e., it is not shadowed
by any local binding, but refers to the imported, read-only, x.

Then we can trust x always to be of type T, so that we can compile

  (display x)

to

  display_T (x);

If x is the string "Hello World!\n", we can go further:

  printf ("Hello World\n");

Note how this depends on the compiler's ability to determine a
constant bond between x and "Hello World\n".  If this binding was
dynamic, we couldn't compile it to the last statement above.

> Similarly, I would have thought a set! could be inlined as
> dispatch_table[scm->type]->set(scm, value);

I don't understand how the above expression relates to the current
discussion.  What is `scm'?  Note that what set! needs to do is to
find the setter which is associated with the getter being used in the
expression.  It is not a question of type dispatch.

> It's not obvious to me how the idea of instead changing the procedure
> object is "more local". A "plain dynamic dispatch set!" would be re-using
> an idea already in the language.

I have not talked about "changing" the procedure.  The proposal means
making a new procedure from the getter and the setter.

With "plain dynamic dispatch set!" I thought you were referring to the
old proposal which meant associating the getter with setter by use of
a procedure property (which, BTW, is not a standard Scheme feature).

Did you refer to something else?

The new proposal is more local because instead of relying on a
mechanism which can associate setters to *any* procedure, we restrict
this bond to procedures constructed with make-procedure-with-setter.

> But what I don't get is why we want to map procedures to setters at
> all.  Surely what we want to do is map types to setters. After all
> it is going to be the type of the object we are trying to set a
> member of which determines which setter to use.
>
> I must be missing something big here.

Yes, this is probably the main cause of the confusion.

The "generalized" set! idea is exactly about mapping getters to a
setters.  Here are two examples of how set! expressions are
transformed:

  (set! (car x) y) --> ((setter car) x y)
  (set! (cdr x) y) --> ((setter cdr) x y)

Obviously, we want (setter cxr) to return set-cxr!.

Note that both setters work on the same type: a pair.
What determines which setter to use is the *getter* we're using.

> > We also avoid associating a setter slot with each procedure.
>
> But who really wants to associate setter slots with procedures?

The old proposal did this (using set-procedure-property!).

> >It's a bit sad that I'm the only one that previously tried to protect
> >the simplicity of the language.
>
> I think it's more a case of everyone having a different idea of what
> protecting the simplicity of the language actually means.

In my case it meant advocating for not extending the set! form, but
require that the first argument is a variable name.

But now I've changed opinion: The new solution doesn't do anything
strange.  It basically adds a constructor (an ordinary procedure)
which takes standard Scheme types (procedures) and return a standard
Scheme type (procedure).  There's no need of a table, and the
extended set! form can be compiled to efficient code.

> My concern is not that your idea is or isn't efficient. It is that
> the whole design seems to rely on being able to modify the guts of
> the system in order to be able to implement efficiently.  If I were
> to take some arbitrary R5RS Scheme, and without modifying it try to
> implement this scheme, I would need some ugly form of table lookup
> or something (I think).

Here's a plain (untested) R5RS implementation:

(define make-procedure-with-setter #f)
(define getter #f)
(define setter #f)

(let ((getter-cookie '(cookie))
      (setter-cookie '(cookie)))
  (set! make-procedure-with-setter
        (lambda (getter setter)
          (lambda args
            (cond ((eq? (car args) getter-cookie) getter)
                  ((eq? (car args) setter-cookie) setter)
                  (else (apply getter args))))))
  (set! getter
        (lambda (proc)
          (proc getter-cookie)))
  (set! setter
        (lambda (proc)
          (proc setter-cookie))))

The set! form in the prototype implementation is already R5RS (except
for the way to write macros).

If anyone can find a better design, which still can be compiled to
efficient code, I'd be very happy to see it.

/mdj