I wrote a page with five ideas to simplify Self (slightly blue plane stuff):
http://www.merlintec.com:8080/software/11
The jargon there is very Self oriented and so might not make much sense to Squeakers. Some of the ideas at least could be applied to Squeak -
1) unified block/method/context objects
in Self, methods are supposed to be prototypes from which we obtain contexts by cloning. In practice it doesn't work that way and this is a call to take the idea seriously. Since Squeak is class based we are already used to the idea that an object and the template from which it is created are very different, so the idea doesn't have the same impact.
2) up arrow as binary selector
Squeak has little special syntax: <- (or :=), ^, self, super, thisContext, true and false. Self has even less and I would like to take advantage of idea 1 and make ^ be just another binary selector. This doesn't apply to Squeak since it doesn't have the "implied self receiver" syntax.
3) simpler syntax: no literals
This idea would work just as well in Squeak and we already have a taste of it in MathMorphs and eToys. If you can embed a graphical representation of any object in source code, you don't need syntax for literals.
4) simpler assignment
This is an attempt to make assignment and message argument binding be the same thing. A side effect would be to allow more flexible keyword messages with default arguments.
5) removing inheritance from the base-level
parent slots are a neat language design "pun" (a single thing is used for both "has-part" and "inherits-from" relations) but it would be better to have all program structuring stuff live exclusively in the meta-level. Squeak doesn't have parent slots, of course, but does have #class, #isKindOf: and other unmarked portals into the meta-level underworld.
===
As I was thinking about the pink plane / blue plane stuff, I came to the conclusion that there is a related but separate dimension. A project can seek "synergy" or it can work towards a "separation of concerns".
If you add a JIT, for example, it might be an optional plug-in that makes things faster when present, like in Jitter 3. Everything can work either with it or without it. An alternative would be to make it a central aspect of the design (like Self or Jikes) and try to see what can be made simpler because of it. Can name spaces be simpler? Modules? 3D graphics?
My personal taste makes me favor synergetic designs but there are problems. It is harder for more than a small group (or even a single person) to work on it. It is harder (impossible?) to understand a part of the project without learning all the rest. New requirements might come along and it might be easier to throw away the system and build a new one than to adapt it.
The "separation of concerns" step-by-step development is the way to go in the pink plane. But what is the best style for Squeak on the blue plane?
-- Jecel
At 8:33 PM -0300 8/19/02, Jecel Assumpcao Jr wrote:
I wrote a page with five ideas to simplify Self (slightly blue plane stuff):
http://www.merlintec.com:8080/software/11
The jargon there is very Self oriented and so might not make much sense to Squeakers. Some of the ideas at least could be applied to Squeak -
- unified block/method/context objects
in Self, methods are supposed to be prototypes from which we obtain contexts by cloning. In practice it doesn't work that way and this is a call to take the idea seriously. Since Squeak is class based we are already used to the idea that an object and the template from which it is created are very different, so the idea doesn't have the same impact.
First, I should say that I really like (a) a lot of the ideas in Self, and (b) the motivation behind trying to do Self. But, I think there is an entity somewhere between classes and Self prototypes (I've been calling them exemplars) and some action somewhere between instantiation and cloning that would work better -- and I'll bet there are several people on this list including yourself that could come up with these better structures.
- up arrow as binary selector
Squeak has little special syntax: <- (or :=), ^, self, super, thisContext, true and false. Self has even less and I would like to take advantage of idea 1 and make ^ be just another binary selector. This doesn't apply to Squeak since it doesn't have the "implied self receiver" syntax.
As Andreas and others before him have pointed out, minimal syntax (a la LISP) is a double edged sword. I personally liked the slightly richer syntax of ST-76, and especially liked the "slightly richer than that" syntax of ST-72. "Uniform with helpful markers" is a good motto here.
- simpler syntax: no literals
This idea would work just as well in Squeak and we already have a taste of it in MathMorphs and eToys. If you can embed a graphical representation of any object in source code, you don't need syntax for literals.
Interestingly, this is even in my Master's thesis on Flex. It just never happened in Smalltalk despite occasional urgings. I have often wished for such a "literal literal" facility in Smalltalk.
- simpler assignment
This is an attempt to make assignment and message argument binding be the same thing. A side effect would be to allow more flexible keyword messages with default arguments.
This is really important, and was much better done in ST-76 and ST-72.
- removing inheritance from the base-level
parent slots are a neat language design "pun" (a single thing is used for both "has-part" and "inherits-from" relations) but it would be better to have all program structuring stuff live exclusively in the meta-level.
Precisely. The problem with parent slots is that they are much too lisp-like for an OOP language. Also, there are many other relationships that are useful (as in CYCL) that should be metalevel implementable.
Squeak doesn't have parent slots, of course, but does have #class, #isKindOf: and other unmarked portals into the meta-level underworld.
We need a complete (or darn close) and real metasystem.
===
As I was thinking about the pink plane / blue plane stuff, I came to the conclusion that there is a related but separate dimension. A project can seek "synergy" or it can work towards a "separation of concerns".
If you add a JIT, for example, it might be an optional plug-in that makes things faster when present, like in Jitter 3. Everything can work either with it or without it. An alternative would be to make it a central aspect of the design (like Self or Jikes) and try to see what can be made simpler because of it. Can name spaces be simpler? Modules? 3D graphics?
My personal taste makes me favor synergetic designs but there are problems. It is harder for more than a small group (or even a single person) to work on it. It is harder (impossible?) to understand a part of the project without learning all the rest. New requirements might come along and it might be easier to throw away the system and build a new one than to adapt it.
The "separation of concerns" step-by-step development is the way to go in the pink plane. But what is the best style for Squeak on the blue plane?
One way to think about this is that you want everything to be separate, but to be easily able to specify dynamic relationships that express the nonlinearities. For example, "a really good thing to do" in Squeak would be to put a vertical line in a method and have a really simple semantic description on the left, and an optional case-based set of optimizations on the right. Run both to test, etc. The case pattern matches constitute the dynamic relationships that "join" the two kinds of descriptions. Similar techniques can be used to implement multiple perspectives, delegation models, and nonlinear event-driven interactions. The whole blueplane idea here is to try to separate strategies from tactics and "ors" from "ands", but to be able to relate them in a clean way when it really helps matters.
Cheers,
Alan
----------
-- Jecel
--
Hi, Alan
Thanks for your thoughts!
First, I should say that I really like (a) a lot of the ideas in Self, and (b) the motivation behind trying to do Self.
BTW, I am thinking of having the official name of what I now call Self/R be "Neo Smalltalk". Would anybody have a problem with that? Even though it will be to Self what Self is to Smalltalk-80, I think it is very much in the spirit of Smalltalk (and I even intend to make it ANSI Smalltalk compatible).
But, I think there is an entity somewhere between classes and
Self prototypes (I've been calling them exemplars) and some action somewhere between instantiation and cloning that would work better -- and I'll bet there are several people on this list including yourself that could come up with these better structures.
Some people think that it is easy to build class-like objects in Self. What is easy is to have shared code (and even shared state) with mixin-like objects. There is no inheritance of "object format" (if class X has instance variables "a" and "b" any subclass will have them as well) and they aren't much good at grouping related objects together.
Note that these things are possible - so much so that Mario Wolczko's Smalltalk-in-Self implementation did it (see "self includes: Smalltalk" http://research.sun.com/self/papers/smalltalk.pdf) using a structure nearly identical to that described in W. R. LaLonde, D. A. Thomas and J. R. Pugh. "An exemplar based Smalltalk." In Proc. of ACM Conf. on Object-Oriented Programming Systems, Languages and Applications, 1986, pp. 322--330.
But on the issue of "an action more like instantiation" I don't have any ideas. What is missing from cloning?
- up arrow as binary selector
As Andreas and others before him have pointed out, minimal syntax (a la LISP) is a double edged sword. I personally liked the slightly richer syntax of ST-76, and especially liked the "slightly richer than that" syntax of ST-72. "Uniform with helpful markers" is a good motto here.
Right, the "less is more" temptation is the very first of the seven deadly sins listed in the paper of that name:
http://www.csse.monash.edu.au/~lindap/research.html
And as Brian Rice pointed out to me, this idea makes you have to add parenthesis when returning expressions with keyword selectors :-(
- simpler syntax: no literals
Interestingly, this is even in my Master's thesis on Flex. It just never happened in Smalltalk despite occasional urgings. I have often wished for such a "literal literal" facility in Smalltalk.
Some people wish for a more declarative Smalltalk. And since the current modules design doesn't handle arbitrary objects, it isn't too friendly towards this idea.
The bear/flower/bee font picture in your 1977 Computer article was my inspiration for this. I discovered that I didn't really want a visual language. And I didn't want a textual language. I need the best of each.
- simpler assignment
This is really important, and was much better done in ST-76 and ST-72.
It is hard to be so flexible with a stack based virtual machine, unfortunately. I am still trying to figure it out how it might be done.
- removing inheritance from the base-level
Precisely. The problem with parent slots is that they are much too lisp-like for an OOP language. Also, there are many other relationships that are useful (as in CYCL) that should be metalevel implementable.
You mean http://www.cyc.com/cycl.html ? It reminds me of Frames, which of course remind me of prototype based languages. It also reminds me of UN's universal network language (UNL - http://www.unl.ias.unu.edu/unlsys/index.html).
One way to think about this is that you want everything to be separate, but to be easily able to specify dynamic relationships that express the nonlinearities. For example, "a really good thing to do" in Squeak would be to put a vertical line in a method and have a really simple semantic description on the left, and an optional case-based set of optimizations on the right. Run both to test, etc. The case pattern matches constitute the dynamic relationships that "join" the two kinds of descriptions. Similar techniques can be used to implement multiple perspectives, delegation models, and nonlinear event-driven interactions. The whole blueplane idea here is to try to separate strategies from tactics and "ors" from "ands", but to be able to relate them in a clean way when it really helps matters.
I agree, but while this is also what the Aspect people are trying to do I am not very happy with their results. One problem is that any textual notation to describe these "join relationships" will be too awkward to be useful for non trivial examples. Another problem is their focus on "blue print" languages (where the whole system is described in the sources - no image files with hand made objects) so that their relations are static and not dynamic like you want.
BTW, I think I already mentioned this here but it seems to me that the most important optimization in expert code is cacheing results. Could you give an example of a method with a simple semantic description and with case based optimizations? I think it is likely that we could transform one into the other simply by tagging certain expressions as being cacheable (we could show them in a different color in a browser).
-- Jecel
On Tuesday, August 20, 2002, at 10:06 Uhr, Alan Kay wrote:
First, I should say that I really like (a) a lot of the ideas in Self, and (b) the motivation behind trying to do Self.
Same here. I do have a feeling that the direction, while very fruitful, turned out to be "too uniform", in some senses. There is no fence at all to the meta-level at one point ( object vs. class ) and still a big one at another ( object vs. code/method ).
But, I think there is an entity somewhere between classes and
Self prototypes
Yes, that is what I have so far as well: the "class equivalents" are somewhere between what we call classes and objects/prototypes, and the "object equivalents" are somewhere between objects and method contexts.
(I've been calling them exemplars)
I like to call them "parametrized objects".
and some action somewhere between instantiation and cloning that would work better
I am still swimming a bit on that one. There is plain "invocation", supplying the required arguments and getting a result. But what is the result? Either (a) "p-object + params" or (b) "result of some message sent to p-object + params".
Answer (a) is closer to normal instance creation, (b) is closer to method invocation.
Then there is currying, which is "p-object + some params", clearly a variant of (a). Interestingly enough, this would be considered a refinement step similar to subclassing, but it could also be considered partial instantiation.
Existing method invocation is a special case of (b): the method is a parametrized object, the method-context holds the parameters, it is then sent the "value" message. This definition is, of course, recursive, but that doesn't bother us, we just cut the recursion off at the primitive level.
- removing inheritance from the base-level
parent slots are a neat language design "pun" (a single thing is used for both "has-part" and "inherits-from" relations) but it would be better to have all program structuring stuff live exclusively in the meta-level.
Precisely. The problem with parent slots is that they are much too lisp-like for an OOP language. Also, there are many other relationships that are useful (as in CYCL) that should be metalevel implementable.
Squeak doesn't have parent slots, of course, but does have #class, #isKindOf: and other unmarked portals into the meta-level underworld.
We need a complete (or darn close) and real metasystem.
How do we know when we have a complete metasystem? There are too many different directions in wich we can abstract. Anyway, the only credible candidate I've seen so far is the research done in software architecture. They start off with "components" and "connectors", and then refine from there.
[..]
The "separation of concerns" step-by-step development is the way to go in the pink plane. But what is the best style for Squeak on the blue plane?
One way to think about this is that you want everything to be separate, but to be easily able to specify dynamic relationships that express the nonlinearities. For example, "a really good thing to do" in Squeak would be to put a vertical line in a method and have a really simple semantic description on the left, and an optional case-based set of optimizations on the right. Run both to test, etc. The case pattern matches constitute the dynamic relationships that "join" the two kinds of descriptions. Similar techniques can be used to implement multiple perspectives, delegation models, and nonlinear event-driven interactions. The whole blueplane idea here is to try to separate strategies from tactics and "ors" from "ands", but to be able to relate them in a clean way when it really helps matters.
Hmm...
Marcel
On Monday 26 August 2002 09:53, Marcel Weiher wrote:
On Tuesday, August 20, 2002, at 10:06 Uhr, Alan Kay wrote:
and some action somewhere between instantiation and cloning that would work better
I am still swimming a bit on that one. There is plain "invocation", supplying the required arguments and getting a result. But what is the result? Either (a) "p-object + params" or (b) "result of some message sent to p-object + params".
Answer (a) is closer to normal instance creation, (b) is closer to method invocation.
In (a) you might have either p-object modified by the parameters or a clone of p-object modified by the parameters. The latter is, as you say, like instanciation while the former is like assignment (another direction I was interested in exploring).
Then there is currying, which is "p-object + some params", clearly a variant of (a). Interestingly enough, this would be considered a refinement step similar to subclassing, but it could also be considered partial instantiation.
Beta uses these similarities to lump everything into a single "pattern" construct.
Existing method invocation is a special case of (b): the method is a parametrized object, the method-context holds the parameters, it is then sent the "value" message. This definition is, of course, recursive, but that doesn't bother us, we just cut the recursion off at the primitive level.
Unless we don't care about reentrant methods, we had better copy them before sending "value". So, once again, this is like instanciation.
We need a complete (or darn close) and real metasystem.
How do we know when we have a complete metasystem? There are too many different directions in wich we can abstract. Anyway, the only credible candidate I've seen so far is the research done in software architecture. They start off with "components" and "connectors", and then refine from there.
A complete metasystem would allow us to implement Squeak in Squeak. We don't have that now - we can implement *another* Squeak in Squeak, which isn't the same thing!
-- Jecel
On Tuesday, August 27, 2002, at 12:03 Uhr, Jecel Assumpcao Jr wrote:
On Monday 26 August 2002 09:53, Marcel Weiher wrote:
On Tuesday, August 20, 2002, at 10:06 Uhr, Alan Kay wrote:
and some action somewhere between instantiation and cloning that would work better
I am still swimming a bit on that one. There is plain "invocation", supplying the required arguments and getting a result. But what is the result? Either (a) "p-object + params" or (b) "result of some message sent to p-object + params".
Answer (a) is closer to normal instance creation, (b) is closer to method invocation.
In (a) you might have either p-object modified by the parameters or a clone of p-object modified by the parameters.
Those aren't the only options. You can also have a "context" object (holding the parameters) refering to the original object, which isn't modified. Just like a method is neither copied nor modified when you call it. It just looks at the MethodContext for parameters.
The latter is, as you say, like instanciation while the former is like assignment (another direction I was interested in exploring).
Then there is currying, which is "p-object + some params", clearly a variant of (a). Interestingly enough, this would be considered a refinement step similar to subclassing, but it could also be considered partial instantiation.
Beta uses these similarities to lump everything into a single "pattern" construct.
Yes, I just looked at Beta the other day, but somehow it didn't seem to be what I am aiming for. I am also not sure that all this is a single construct, but rather a continuum of related ones.
Existing method invocation is a special case of (b): the method is a parametrized object, the method-context holds the parameters, it is then sent the "value" message. This definition is, of course, recursive, but that doesn't bother us, we just cut the recursion off at the primitive level.
Unless we don't care about reentrant methods, we had better copy them before sending "value". So, once again, this is like instanciation.
Quite the contrary, re-entrant code is achived without making copies of methods all the time. In fact, I strongly believe that instantiation is too complex/messy/procedural most of the time using just the example of methods/functions: just imagine that everytime you wanted to call a method, you'd have to make a copy first, then mutate that copy and finally send value to it!
We need a complete (or darn close) and real metasystem.
How do we know when we have a complete metasystem? There are too many different directions in wich we can abstract. Anyway, the only credible candidate I've seen so far is the research done in software architecture. They start off with "components" and "connectors", and then refine from there.
A complete metasystem would allow us to implement Squeak in Squeak. We don't have that now - we can implement *another* Squeak in Squeak, which isn't the same thing!
I am not sure I am getting the distinction you are making here.
Marcel
On Monday 26 August 2002 19:16, Marcel Weiher wrote:
On Tuesday, August 27, 2002, at 12:03 Uhr, Jecel Assumpcao Jr wrote:
In (a) you might have either p-object modified by the parameters or a clone of p-object modified by the parameters.
Those aren't the only options. You can also have a "context" object (holding the parameters) refering to the original object, which isn't modified. Just like a method is neither copied nor modified when you call it. It just looks at the MethodContext for parameters.
That is an implementation optimization, not really another option. If the system automatically avoids copying immutable state then I don't have to think about these kinds of details.
Yes, I just looked at Beta the other day, but somehow it didn't seem to be what I am aiming for. I am also not sure that all this is a single construct, but rather a continuum of related ones.
Your impression is correct.
Unless we don't care about reentrant methods, we had better copy them before sending "value". So, once again, this is like instanciation.
Quite the contrary, re-entrant code is achived without making copies of methods all the time. In fact, I strongly believe that instantiation is too complex/messy/procedural most of the time using just the example of methods/functions: just imagine that everytime you wanted to call a method, you'd have to make a copy first, then mutate that copy and finally send value to it!
That is the story we tell (but don't actually implement ;-) in Self: contexts (called activation objects) are created by cloning methods and then changing them.
A complete metasystem would allow us to implement Squeak in Squeak. We don't have that now - we can implement *another* Squeak in Squeak, which isn't the same thing!
I am not sure I am getting the distinction you are making here.
Imagine that you are happily running Squeak on your machine. Let's call that sq1. Then you execute something like
(InterpreterSimulator new openOn: 'example.image') test
and you get a second Squeak (let's call it sq2) running much more slowly inside the first one. By using a browser in sq1 you can change any aspect of sq2 that you want. But the changes you can make to sq1 are far more limited.
Some examples: you can change the header format for objects in sq2, how it handles messages, primitives, the meaning of bytecodes, etc.
As the meta-level for sq1 becomes better and better, the smaller the gap will be between what can be changed in sq1 and sq2.
-- Jecel
On Tuesday, August 27, 2002, at 02:19 Uhr, Jecel Assumpcao Jr wrote:
Those aren't the only options. You can also have a "context" object (holding the parameters) refering to the original object, which isn't modified. Just like a method is neither copied nor modified when you call it. It just looks at the MethodContext for parameters.
That is an implementation optimization, not really another option. If the system automatically avoids copying immutable state then I don't have to think about these kinds of details.
Sorry, but I also find "invoke with arguments" easier to *think* about than "make a copy, modify the copy, do something with the copy", essentially because it is declarative. In fact, I currently see/use it the other way around: making a copy and munging that is a (hidden) implementation of a parametrized object.
This is pretty much where I think self went off in the wrong direction, unifying too many different (if related) ideas with low-level/imperative/time-dependent mechanisms and then having to untangle the mess again, with great difficulty.
Yes, you can get away with tossing things like classes (and method invocation), but while an interesting intellectual exercise, you aren't actually helping your users, IMHO.
Unless we don't care about reentrant methods, we had better copy them before sending "value". So, once again, this is like instanciation.
Quite the contrary, re-entrant code is achived without making copies of methods all the time. In fact, I strongly believe that instantiation is too complex/messy/procedural most of the time using just the example of methods/functions: just imagine that everytime you wanted to call a method, you'd have to make a copy first, then mutate that copy and finally send value to it!
That is the story we tell (but don't actually implement ;-) in Self: contexts (called activation objects) are created by cloning methods and then changing them.
Once again, that seems like backwards to me: turning a simple invocation into something conceptually more complex (time-dependent etc.), then optimizing that away again.
Why not present the simple model to the user?
Marcel
On Tuesday 27 August 2002 05:45, Marcel Weiher wrote:
Sorry, but I also find "invoke with arguments" easier to *think* about than "make a copy, modify the copy, do something with the copy", essentially because it is declarative. In fact, I currently see/use it the other way around: making a copy and munging that is a (hidden) implementation of a parametrized object.
We will have to agree to disagree, then :-)
I see making a copy as changing the semantics. Without a copy, two different invocations of a method would share temporary variables and arguments. With a copy, each one has a separate state.
Of course, the declarative "invoke with arguments" will almost certainly have implicitly in its semantics some form of copying. I am just stating explicitly that this is what is happening.
This is pretty much where I think self went off in the wrong direction, unifying too many different (if related) ideas with low-level/imperative/time-dependent mechanisms and then having to untangle the mess again, with great difficulty.
Who has to untangle the mess? The user or the implementor? If only the latter, then I don't see the problem.
Yes, you can get away with tossing things like classes (and method invocation), but while an interesting intellectual exercise, you aren't actually helping your users, IMHO.
It is great fun to grab some object on the screen and then patch it until it is a new thing. Fun is important and helps users.
That is the story we tell (but don't actually implement ;-) in Self: contexts (called activation objects) are created by cloning methods and then changing them.
Once again, that seems like backwards to me: turning a simple invocation into something conceptually more complex (time-dependent etc.), then optimizing that away again.
Why not present the simple model to the user?
Because I think this is the simple model. Compare this with the model presented in the Blue Book (which can also be seen in the Interpreter class in Squeak). 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!
There are several level of models. Let us make sure we are comparing comparable ones.
-- Jecel
On Tuesday, August 27, 2002, at 08:53 Uhr, Jecel Assumpcao Jr wrote:
On Tuesday 27 August 2002 05:45, Marcel Weiher wrote:
Sorry, but I also find "invoke with arguments" easier to *think* about than "make a copy, modify the copy, do something with the copy", essentially because it is declarative. In fact, I currently see/use it the other way around: making a copy and munging that is a (hidden) implementation of a parametrized object.
We will have to agree to disagree, then :-)
Sure.
I see making a copy as changing the semantics.
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.
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.
With a copy, each one has a separate state.
You don't need a copy for that, just a proper lookup algorithm.
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 ;-)
This is pretty much where I think self went off in the wrong direction, unifying too many different (if related) ideas with low-level/imperative/time-dependent mechanisms and then having to untangle the mess again, with great difficulty.
Who has to untangle the mess? The user or the implementor?
Both.
If only the latter, then I don't see the problem.
Since it's both, I do see a problem. Maybe I can make you see it as well, but I probably can't explain it well enough...
Yes, you can get away with tossing things like classes (and method invocation), but while an interesting intellectual exercise, you aren't actually helping your users, IMHO.
It is great fun to grab some object on the screen and then patch it until it is a new thing. Fun is important and helps users.
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.
That is the story we tell (but don't actually implement ;-) in Self: contexts (called activation objects) are created by cloning methods and then changing them.
Once again, that seems like backwards to me: turning a simple invocation into something conceptually more complex (time-dependent etc.), then optimizing that away again.
Why not present the simple model to the user?
Because I think this is the simple model.
Simple from an implementation point of view (all handled via one mechanism). But I disagree that this is simple for the user. Before I give my reasoning, let me first reiterate that I agree that constructing objects (once) is simple for users, the question is where to go from there. Let me also try to clarify what I mean by "simple": reducing the cognitive load for a given construct as much as possible.
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.
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.
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.
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...
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.
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.
There are several level of models. Let us make sure we are comparing comparable ones.
Definitely. I am talking about user-level models.
Marcel
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). 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.
Not making a copy at all will give you a different behavior. In particular:
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. 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.
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 addresses for "count" and "max", and each one will copy 0 and 99 to 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.
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. 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.
Simple from an implementation point of view (all handled via one mechanism). But I disagree that this is simple for the user. Before I give my reasoning, let me first reiterate that I agree that constructing objects (once) is simple for users, the question is where to go from there. Let me also try to clarify what I mean by "simple": reducing the cognitive load for a given construct as much as possible.
Ok.
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).
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.
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. In fact, if you allow lexical scoping you can actually get by without inheritance as in Beta or E. I am trying to convince a friend who is designing a language inspired on Tim Budd's Leda to go in this direction. My own proposal was in a different direction, but that doesn't mean I consider it better.
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.
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. 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.
If there is a bug, then he will be thrown into the debugger and a lower level model will be needed to cope with it. That would be the cloning in Self and the Blue Book in Smalltalk.
The real implementation can use an even lower level model which can be very different from the previous one. It isn't in Squeak, but it is in VisualWorks, StrongTalk or even Smalltalk V. And it is likely to be a different model in future versions of Squeak (see Anthony Hannan's work in that direction).
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 ;-)
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.
Cheers, -- Jecel
I wrote:
You have to properly initialize the stack frame. 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.
Most people on this list probably aren't interested in the fine details of C semantics, but after I sent the above it started to look strange to me so I made a quick test and found I was wrong.
For dynamic variables, initializer expressions are just a short hand for code to execute the implied assignment and the expression is *not* evaluated at compile time. So
int gg = 12;
void foo() { int total = gg*2; ...... }
main() { foo(); gg = 45; foo(); ... }
will use the value of "gg" at the time foo is invoked, unlike what I had stated. What I wrote is correct for static variables, so this is a case of same syntax but two different semantics. I guess this makes sense in practice as we could "#define gg 12" to get the effect of evaluating the expression at compile time.
-- Jecel
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
When I do:
ATest _ WorldWindow new openInWorld.
I get a really messed up system window.
The class comment which is there (YAY!!!) says it should have a red bar which changes color when it is given focus.
It does the exact same thing with my clean control image.
Does anyone else have the same problem?
On Thursday 29 August 2002 06:01 pm, Alan Grimes wrote:
When I do:
ATest _ WorldWindow new openInWorld.
I get a really messed up system window.
The class comment which is there (YAY!!!) says it should have a red bar which changes color when it is given focus.
It does the exact same thing with my clean control image.
Does anyone else have the same problem?
I'm not sure that that's even used any more. And even if it were, I don't believe that you're using it the way it wants to be used... I think it needs to have an internal World. Take a look at the examples on the class side (but save your image first, because you won't be able to escape the inner world!).
And, as you note, its layout is messed up.
ProjectViewMorph (inside a SystemWindow) and EmbeddedWorldBorder seem to be used currently.
You can see how these operate by creating a new Morphic Project and then returning to the previous project, where you should see (depending on your Preferences) a little SystemWindow with a thumbnail of the world you just left. This thumbnail is a ProjectViewMorph.
If you click-and-hold the red button over the thumbnail (perhaps moving the mouse a tiny bit after a while), a menu will come up eventually. Choose "ENTER ACTIVE" and you'll see the world-in-world stuff that's currently used (an EmbeddedWorldBorder with a scaled view of the world inside it).
What are you trying to do with the WorldWindow?
I seem to be having problems building a vm after installing Mac OS X 10.2 (Jaguar) and the new development tools. On interp.c, I get bugs starting with the line defining "int bool;" and apparently cascading therefrom.
Has anybody else experienced this problem or found a solution?
On Friday, August 30, 2002, at 03:51 Uhr, Andrew C. Greenberg wrote:
I seem to be having problems building a vm after installing Mac OS X 10.2 (Jaguar) and the new development tools. On interp.c, I get bugs starting with the line defining "int bool;" and apparently cascading therefrom.
Hmm, I didn't actually have problems in interp.c (did you gnuify?).
Anyway, the best way to go for now is probably to tell ProjectBuilder not to use gcc3, telling it to use gcc2.95 instead. There is a special "GCC Compiler Settings" Tab.
Marcel
On Friday 30 August 2002 01:44 am, Marcel Weiher wrote:
On Friday, August 30, 2002, at 03:51 Uhr, Andrew C. Greenberg
wrote:
I seem to be having problems building a vm after installing Mac OS X 10.2 (Jaguar) and the new development tools. On interp.c, I get bugs starting with the line defining "int bool;" and apparently cascading therefrom.
Hmm. bool is a reserved word for C99, I believe (see stdbool.h). We should probably change the Slang source.
Try to tell your compiler that the source is plain _OLD_ C (i.e. not C99). Or see if 2.95 works for you, as Marcel suggested.
Or, just have gnuify fix the problem:
--- ./platforms/unix/config/gnuify Sat May 4 22:50:08 2002 +++ newgnuify Fri Aug 30 07:42:13 2002 @@ -45,6 +45,8 @@ stage= 0; }
+/<bool>/ { gsub(/<bool>/, "safebool") } + /#include "sq.h"/ { print "#include "sqGnu.h"\n"; next; @@ -140,6 +142,7 @@ next; }
+ # default { print;
On Friday, August 30, 2002, at 04:44 Uhr, Ned Konz wrote:
Try to tell your compiler that the source is plain _OLD_ C (i.e. not C99). Or see if 2.95 works for you, as Marcel suggested.
...especially as this will also avoid the 40% de-optimzation that GCC 3.1 brings for Squeak. I just tried it out myself, and it seems to be true. ( only 40 M bytecodes/s for 3.1 vs. over 70 M bytecodes/s for 2.95)
So: use 2.95, 3.1 simply isn't usable for Squeak (yet).
Marcel
Ned Konz ned@bike-nomad.com is claimed by the authorities to have written:
Hmm. bool is a reserved word for C99, I believe (see stdbool.h). We should probably change the Slang source.
Try to tell your compiler that the source is plain _OLD_ C (i.e. not C99). Or see if 2.95 works for you, as Marcel suggested.
Or, just have gnuify fix the problem:
I'd prefer a fix in the Slang - a similar problem bites on Acorn where the standard OSLib defines booleans.
tim
I am going to heavily edit this so it doesn't become another #removeAll: thread (where, btw, I agree with Richard)
On Thursday 29 August 2002 18:52, Marcel Weiher wrote:
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).
Sorry about that - I wrote another message ("c correction") explaining that I was wrong and you were right about C. I guess I have been jumping back and forth between to many different languages lately :-)
I am saying that I want something like (1) to be usable with objects as well as methods.
Yes, that is what am interested in knowing more about. If I have a class with "a" and "b" parameters, and "c" and "d" regular instance variables, what should the values of "c" and "d" be for a new instance? The Smalltalk way would be for them to be initialized to nil, I suppose. Since the new object creation looks like a message send, can I think of it as a contructor (in the C++ sense) with code that can set "c" and "d" in the new instance?
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?
It is used by the user when dealing with the debugger.
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.
I would guess he was talking about this paper:
W.R. LaLonde, D. Thomas, and J.R. Pugh. "An Exemplar Based Smalltalk". In Proceedings of the 1st Conference on Object-Oriented Programming Systems, Languages and Applications (OOPSLA '86), Portland, Oregon, ACM Sigplan Notices (21)11 , pp. 322--330, 1986.
I haven't read this paper since 1987, so while I can't remember any significant difference between this and Mario's Smalltalk-in-Self that doesn't mean that there weren't any.
-- Jecel
On Saturday, August 31, 2002, at 12:36 Uhr, Jecel Assumpcao Jr wrote:
I am saying that I want something like (1) to be usable with objects as well as methods.
Yes, that is what am interested in knowing more about. If I have a class with "a" and "b" parameters, and "c" and "d" regular instance variables, what should the values of "c" and "d" be for a new instance?
Before I start answering, just a short note to the status of this work: it is currently part of a system built on top of an existing language (Objective-C, though ST would make parts easier), not a new language. I am groping forward very slowly, because the aim is to build something that is useful in the context of the system...
At present, parameters aren't aren't associated with classes as such. Instead parameterized objects (templates) are created from existing objects by adding slots. A slot has a name and a key-value path to the object it replaces. They key-value path means that the replacement can affect sub-structure.
I can currently get away with not copying the object because in the current system, all I actually do with the object that is relevant is output it to a graphics-stream, so the context-evaluation takes place on output only. I probably will have to change that in the near future, though.
To avoid copying in that case, I would either need (a) proxy-objects that get placed in the slots or (b) a more general languge mechanism that adds a lookup-layer, but that probably isn't going to happen in Objective-C.
The Smalltalk way would be for them to be initialized to nil, I suppose. Since the new object creation looks like a message send, can I think of it as a contructor (in the C++ sense) with code that can set "c" and "d" in the new instance?
I wouldn't do that, even though it may look a bit like it at first.
The thing to understand is that this isn't "new object creation". It is "definition". Object A *is* Parametrized Object B with parameters c=C and d=D. Think of it as the difference between putting a literal object someplace vs. code that constructs the object.
For example, constructores always create an object. This is not necessarily the case here. If Parametrized Object B has parameters c and d, and I only give it a value for parmeter c, then I don't get a fully funcioning instance, I get a new parametrized object with parameter d. (That's the 'currying' aspect I was refering to).
Furthermore, it is always possible to turn any object into a parametrized object by adding a slot to it (the slot turns an attribute or sub-structure into a parameter), so there is a back and forth between levels of abstraction (or meta-levels). I also call this little engine the "AbstractionEngine".
The back-and-forth aspect also means that you can create long reference chains, with a parameter being introduced and bound over and over again. There probably needs to be a mechanism to simplify such chains, but that problem is a long way off for now.
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?
It is used by the user when dealing with the debugger.
So the debugger synthesizes a view of the system that doesn't exist otherwise?
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.
I would guess he was talking about this paper:
W.R. LaLonde, D. Thomas, and J.R. Pugh. "An Exemplar Based Smalltalk". In Proceedings of the 1st Conference on Object-Oriented Programming Systems, Languages and Applications (OOPSLA '86), Portland, Oregon, ACM Sigplan Notices (21)11 , pp. 322--330, 1986.
Thanks for the reference!
I haven't read this paper since 1987, so while I can't remember any significant difference between this and Mario's Smalltalk-in-Self that doesn't mean that there weren't any.
I'll check it out.
Regards,
Marcel
On Saturday 31 August 2002 06:38, Marcel Weiher wrote:
The thing to understand is that this isn't "new object creation". It is "definition". Object A *is* Parametrized Object B with parameters c=C and d=D. Think of it as the difference between putting a literal object someplace vs. code that constructs the object.
For example, constructores always create an object. This is not necessarily the case here. If Parametrized Object B has parameters c and d, and I only give it a value for parmeter c, then I don't get a fully funcioning instance, I get a new parametrized object with parameter d. (That's the 'currying' aspect I was refering to).
Ok, this *is* different from what I was thinking. The terms you are using remind me of "parameter based CAD" systems. Those (starting with the original Sketchpad) have paremeters and constraints between parameters, however.
Furthermore, it is always possible to turn any object into a parametrized object by adding a slot to it (the slot turns an attribute or sub-structure into a parameter), so there is a back and forth between levels of abstraction (or meta-levels). I also call this little engine the "AbstractionEngine".
By "adding a slot to it" do you mean modifying the original object or creating a new ID that points to the old object and also to the slot?
The back-and-forth aspect also means that you can create long reference chains, with a parameter being introduced and bound over and over again. There probably needs to be a mechanism to simplify such chains, but that problem is a long way off for now.
It should be a simple problem to flatten these chains, though as I indicated in my question above the exact nature of these chains isn't clear to me.
So the debugger synthesizes a view of the system that doesn't exist otherwise?
Exactly. Though note that a Self system implemented as a simple interpreter would have that view actually exist as the interpreter's state. On the other hand, in a Squeak implemented as a sophisticated Jitter the "blue book" view wouldn't normally exist and have would have to be faked by the debugger. So this is an implementation issue, not a language one.
-- Jecel
On Monday, September 2, 2002, at 10:25 Uhr, Jecel Assumpcao Jr wrote:
On Saturday 31 August 2002 06:38, Marcel Weiher wrote:
The thing to understand is that this isn't "new object creation". It is "definition". Object A *is* Parametrized Object B with parameters c=C and d=D. Think of it as the difference between putting a literal object someplace vs. code that constructs the object.
For example, constructores always create an object. This is not necessarily the case here. If Parametrized Object B has parameters c and d, and I only give it a value for parmeter c, then I don't get a fully funcioning instance, I get a new parametrized object with parameter d. (That's the 'currying' aspect I was refering to).
Ok, this *is* different from what I was thinking.
What were you thinking?
The terms you are using remind me of "parameter based CAD" systems.
Yes, the association is an obvious one, especially since I am also doing graphics-related work.
Those (starting with the original Sketchpad) have paremeters and constraints between parameters, however.
Yes, constraints will probably be one of the mechanisms I implement, because they also have nice declarative properties and can be incrementally derived from static objects. (A value being the "most restricted" constraint, which can be "loosened" into a formula).
Furthermore, it is always possible to turn any object into a parametrized object by adding a slot to it (the slot turns an attribute or sub-structure into a parameter), so there is a back and forth between levels of abstraction (or meta-levels). I also call this little engine the "AbstractionEngine".
By "adding a slot to it" do you mean modifying the original object or creating a new ID that points to the old object and also to the slot?
More of the latter than the former, though in practice it gets a bit muddled. I have "template" objects that refer to the original object and manage the slots. New slots can be added to a template, so the original object isn't modified, but the template is. As I said, this is a bit muddled.
The back-and-forth aspect also means that you can create long reference chains, with a parameter being introduced and bound over and over again. There probably needs to be a mechanism to simplify such chains, but that problem is a long way off for now.
It should be a simple problem to flatten these chains,
Yes, flatten and prune. Once perfectly pruned, you sort of get classes (I think), because the prototypical nature of the originals has been pruned away.
though as I indicated in my question above the exact nature of these chains isn't clear to me.
It can probably be thought of as being similar to a reified "cloning-history" of an object, except that the individual steps can also make an object abstract or concrete again.
Marcel
squeak-dev@lists.squeakfoundation.org