logo       

Re: cloning/invocation: msg#01350

lang.smalltalk.squeak.general

Subject: Re: cloning/invocation


On Wednesday, August 28, 2002, at 11:20 Uhr, Jecel Assumpcao Jr wrote:

On Tuesday 27 August 2002 18:23, Marcel Weiher wrote:
Not if it can be hidden, just like you hide *not* making the copy. I
wonde why you see one as changing semantics, and its inverse as not
changing the semantics.

We are talking about an object with a mutable part (prototype for
temporary variables and arguments) and an immutable part (bytecode,
sources, literals, etc).

Er, no. A *method* in Smalltalk and a C/Pascal function consists only of the parts you describe as "the immutable part". Values for arguments are not defined inside the method, they are provided externally in a method context / stack-frame, which is not part of the method. Both together are necessary for the invocation/execution to work.

Values for temporary variables are also not copies of the method, they are initialized by code running in the method. Regarding any assignment statement that is ever executed in a method as "copying the method" is an 'interesting' point of view, but not one that I find particularly compelling.

Either making a full copy or a partial copy
that shares the immutable part will give you results that behave in the
exactly the same way.

You are calling something a copy that isn't a copy. When I pass arguments into the function, I am not making a copy of anything that is inside the function. Quite the contrary, I am providing information to the function that lives *outside* the function.

Not making a copy at all will give you a different behavior. In
particular:

No.

Without a copy, two
different invocations of a method would share temporary variables
and arguments.

Oh, please! Every C function, Pascal procedure/function, Smalltalk
method, etc. manages to do this without making a copy of the method,
simply by externalizing the mutable state.

You have to properly initialize the stack frame.

Exactly. You create a *new* stack frame and then initialize it with *new* values.

How do you do that? By
copying some values from the code or some other area.

int foo ( char c ) {
int count = 0; max = 99;

while ( count < max ) { .... }
}

Some initial code in "foo" must copy 0 and 99 to the right places on the
stack.

No, not even with your somewhat creative definition of "copying", which only applies to literals anyway. To make this clear, consider the following example:

int foo( int arg1, int arg2 )
{
return arg1 + arg2;
}

See, no copying of the function at all, but I still have invoked a function and passed it parameters.


With a copy, each one has a separate state.

You don't need a copy for that, just a proper lookup algorithm.

Two different invocations of "foo" will allocate different memory

Yes, allocate a *different* memory.

addresses for "count" and "max", and each one will copy 0 and 99 to

Er, not really. See above.

those memory addresses. We can make them not copy by doing

static count = 0; max = 99;

but now the two invocations will step on each other's toes.

That has nothing to do with it.

Of course, the declarative "invoke with arguments" will almost
certainly
have implicitly in its semantics some form of copying.

No, it is creating a new method context, and leaving the method
alone. Nothing is being copied.

I am just stating explicitly that this is what is happening.

Well, then you are explicitly wrong about this ;-)

I hope that the C example will make what I am trying to say clearer.

Yes, it did. It made it clear that you are (a) using a somewhat creative point of view wrt 'copying' and (b) still wrong, even if I accepted that POV (which I don't).

Yes. I am fully behind the importance of interactive object
construction, but I am convinced it doesn't go far enough. In fact,
I want to make it even better than it is.

So do I. In the message that started this thread I was proposing making
changes to Self that I thought might make it better.

Yup.

With copying and modifications, you have to remember and be aware of
the history of an object to know what it is like now. This makes the
whole thing more complex. Once again, I believe there is a reason we
*write* method invocation (procedure calls) as:

(1) result := someObject method: arg1 more:arg2.

and not:

(2) method := MethodProtype copy.
method setArg1: arg1.
method setArg2: arg2.
method invoke.

This seems 'obviously' more complex to me.

I would like the system to have both. A base-level programmer would
write (1) while a meta-level programmer could do something like (2).

I don't see any reason why they would want to do that, but that is not at all what I am talking about.

I am saying that I want something like (1) to be usable with objects as well as methods.

However, we do use object instance this way (2). As far as I can
see, self says that (2) is really the unifying mechanism, but
provides both syntax and implementation-optimizations to use (1)
where
possible/necessary.

You are right. Just like the exact same syntax

obj x

will return a non cloned object if "x" is a data slot but will cause all
the cloning we have been talking about if "x" is a method slot.
Two
different semantics for the same syntax. It is an imperfect shortcut,
but nobody seems to have a problem with it.

Well, I have a bit of a problem with a "unifying mechanism" that is used neither (a) by the user nor (b) by the implementation. If there is no actual leverage, maybe the mechanism isn't actually useful?

My current approach is to say that (1) is the unifying mechanismmm
and potentially use (2) internally where necessary (though I'd rather
it were not necessary). Objects, which can be constructed with all
the fun of self-style, can be turned into parametrized objects (which
have some class-like characteristics) by turning some of their
instance variables into parameters. They are then incomplete, and
know that they're incomplete. The user also knows that and exactly
how they are incomplete. To be used, a parametrized object must be
provided with a context that provides the bindings for the
parameters.

That sounds good.

Glad you like it :-)

In fact, if you allow lexical scoping you can actually
get by without inheritance as in Beta or E.

Could very well be. However, I am not in the "I have one construct that can do everything" business. I call that "Turing-envy". ;-) I am more interested in finding useful/usable mechanisms that might have a chance at tackling the "abstraction problem" I think we have in CompSci (in addition to some strange asymmetries in OO).

We can always refactor later ;-)

I am trying to convince a
friend who is designing a language inspired on Tim Budd's Leda to go in
this direction.

Interesting! I am currently busier actually trying this construct ( constructed objects -> parametrization -> use of parametrized objects) in real-world situations, with existing languages.

My own proposal was in a different direction, but that
doesn't mean I consider it better.

I was just following up on Alan's comments about "Exemplars". Not sure what he has in mind there, but it does sound similar in some respects.

Compare this with the model
presented in the Blue Book (which can also be seen in the
Interpreter class in Squeak).

Ahh, but this really is an implementation...

Not necessarily, and that is one reason we are having such a hard time
understanding each other.

Hmm...a byte-coded interpreter seems like a very specific implementation of the message-passing object system that is Smalltalk. But I do think that different levels are part of the problem..

You will probably tell me that the "normal"
Smalltalker doesn't have to learn about stack pointers, method
headers, context objects, etc. Then I will say that the normal
Selfish programmer doesn't have to know that activation objects are
obtained from cloning method objects!

...whereas you've said that cloning is the "model" you present, and
that the implementation doesn't actually do this.

Exactly. There are levels upon levels.

But (see above) it seems to me that there is a level in the middle that isn't actually productive, because its model is neither used in the implementation nor presented to the user. What's it doing there?

At the very top level we can
think of a user typing expressions in a workspace and doing "print it".
He can have a model like you described either in Smalltalk or in Self.

Sure. Though I'd actually try to limit "typing of expressions" and doing "print it" as much as possible. I'd much rather connect reasonably smart components, and resort to expressions (preferably just constraints) only when absolutely necessary.

[snip]

And of course, this implementation might run on a processor that
pretends to execute x86 instructions. And so on. Levels upon levels
until we finally get to the turtles ;-)

Yes, you should never forget about the turtles ;-)

Anyway, this is all too low-level for me anyhow, and method
invocation is not really what I am talking about. It is making
dealing with objects more like method-invocation (or function
evaluation, procedure call) rather than vice versa, making method
invocations more like munging objects.

As I wrote above, I am big fan of languages which take this idea
seriously even if I didn't adopt it myself.

Great!

Marcel





<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

News | FAQ | advertise