[self-interest] Re: Method are not objects?

Jecel Assumpcao Jr jecel at merlintec.com
Thu Oct 26 17:08:50 UTC 2000


Richard wrote:
> > ["writing methods"  is at a different level] 
> Is it? Then it isn't possible to have write slots for methods?

No, but you can have writable parent slots:

     ( | dad* = traits clonable.
         mom* <- ( | doOp = ( x: x + 1 ) | ).
         x <- 0
     | )

We get a copy of this and call it k.

    k x. "this should return 0"
    k doOp.
    k x. "this is now 1"
    k mom: ( | doOp = ( x: x * 10 ) | ).
    k doOp.
    k x. "this is now 10"

So while I can't write to the doOp method slot (at least not with
normal message sends. We can change it with primitives or mirrors) I
can change the mom slot to switch doOp between a few pre-written
methods.

> It seems to me that accessing methods has to be on the same level as
> writing methods and because of consistency over the unified read data/
> eval method concept in Self, you should have writing methods at the
> same level as writing data. But then, I would disagree that _Define
> and _Migrate belong on the same level of abstraction; the ability to
> create new slots should not be invisible to programmers the way that
> clone families should be.

_Migrate is something that I stole from the Apertos reflective
operating system. Though I have used it in some papers, no Self has yet
implemented this.

> Of course. But time and again, I insist that a facility be present purely
> for symmetry and elegence. It need not be used often or implemented in
> the same way, or even at all efficient, so long as it is present. Even
> if runtime creation of methods is rarely used, I believe it should be
> there.

The only "runtime creation of method" that makes sense in Self is to
have the user type in some text and use that as the body for a new
method. This is not at the same level as what you can do with Lisp or
Logo (where you can get some code, transform it in various ways and
then run it).

> If a slot contains data then upon 'reading' the slot returns itself.
> If a slot contains a method object then upon 'reading' the slot, it
> does what you say. I am claimmng that there should be a slot which
> returns an associated slot's method object (raw, as it were) and,
> just as asymmetrically, decompiles the data slot. In both cases, the
> slots return the same thing (either what looks like the result of
> evaluating an expression or a method object) and whether it's a
> data slot or method slot changes only the local storage of what's
> in the slot, not what you can do with it.

That certainly is a possible design. I'll make more comments on this
kind of thing below.

> > And note that you can have a read slot without the
> > corresponding write slot - that is how you indicate constants in the
> > language.
> 
> Yes, but that may be a mistake. After all, x: is still a special
> slot (specially connected to x) regardless of whether it exists
> or not.

My example does have a "mom:" slot but no "dad:" slot. In fact, I can
add a "dad:" slot to that object that has a normal method that does
something other than change the corresponding "dad" constant data slot.

> I think that either any slot must be able to write to
> any other slot, or that slots should /explicitly/ (instead of
> only implicitly) come in pairs/triples.

Again, this is a valid option. And note that if you implement one
design in the language, you can support an alternative in the
programming interface. So you can have it both ways at the same time.

> The way it is now, Self
> expresses public/private by the existence or non-existence of
> the associated write slots. Isn't public/private being dropped
> from Self because it is redundant? I don't mind the absence of
> a write slot denoting a constant but what does the absence of
> a read slot (and the presence of a write slot) denote?

The "public/private" thing in previous versions of Self affected the
message lookup algorithm. Private slots would be found when looking up
messages sent to self, but not for messages sent by other objects.
That, combined with parent priorities and other things, turned out to
complicate things and was dropped until a better design can be found.
There is no relation with write slots at all (except that the privacy
of the data and write slots could be set separately for some
interesting effects).

Marko Mikulicic wrote:
>       (|a| a: 3 . (|| a: 4) . a print )
> "Self VM error: inner methods are no longer supported, slot-list
> within a sub expression is not legal on line 1, character 26 "
>
> This is another inconsistency.

In earlier versions of Self this would have worked. Any (...) would
create an "inner method" which would be evaluated giving the expected
result:

              3 * ( 4 + 1 )

For the common case of just simple subexpressions with no local slots,
however, these inner methods took up a lot of memory. So I suggested
that the parser could eliminate them while generating the bytecodes.
David Ungar listed some interesting reason why he liked inner methods,
yet they were entirely eliminated (in Self 3, if I remember correctly).
Note that the following should work:

    (|a| a: 3. [ | | a: 4] value. a print)

Which brings me to an important point - neither you nor Richard have
said anything about blocks. They are a major complication in the
language and have everything to do with methods automatically
evaluating themselves whenever they are referenced instead of just when
you want them to.

An improved Self (read: simpler!) should somehow unify blocks and
non-autoevaluate methods.

Richard wrote:
> Marko Mikulicic wrote:
> >     1. If methods are so "volatile", they cannot be taken in hand,
> > touched, handled, from where did all these method come from? answer:
> > with slot initialization. The slot initialization syntax is then
> > something different, special. The mirrors are something extern to
> > the language and can be useful to handle, for example, names of
> > slots in a debugger, but not something so essential as methods.
>
> I think you make a very good point. Until now, I had set aside my
> unease with initialization syntax; I don't think I'll be doing this
> anymore. :-)

Object creation would have been complicated back in the text based days
of Self without the special syntax. Now that everything is graphical,
this is not such a big issue.

But "ex nilo" creation of objects *is* a big issue in prototype based
languages. If all objects are created from copying and changing other
objects, where did the first one come from? And how do you express this
"changing an object"?

Self's primitives, such as _AddSlots:, have the unfortunate feature of
not solving this problem. They take an object as an argument (and where
did *that* come from?):

    ( | x <- 10 | ) _AddSlots: ( | y <- 15. parent* = traits point | )

If we had something more like "_AddSlotNamed:WithValue:" then it would
be possible to bootstrap the system without a parser that started out
knowing the slot initialization syntax.

> Does Self have special syntax in x: slots to indicate assignment of a
> value to the x slot, or is it automatic that if a slot x exists then
> x: /will/ assign its parameter to the slot x? (You know, I'd
> completely forgotten that x: could just be a one-parameter method.) If
> it's the latter then it doesn't make sense to ask for the method
> associated with that slot. OTOH, if special syntax is used in x: slots
> then the same scheme should be used in x! slots.

In theory you could write

    ( | x = 10. x: = <- | )

instead of

   ( | x <- 10 | )

I don't have my Sun around here to test it, but tinySelf 0 complains
about a syntax error in the first version. You can write

   ( | x = 10. x: = ( | :arg | arg * arg ) | )

so you can see that the association between "x" and "x:" isn't entirely
automatic.

-- Jecel



More information about the Self-interest mailing list