Hi! Self interested people!
I used to run Self 4.0 on a Sun system. It was kinda fun, but a bit limited in some ways- portability was quite poor; and some of the language concepts were a bit more irregular than I would have liked.
Here are some of the ideas I have to improve it; I'd welcome some comments:
a) 'references'
Consider:
a: b + 1
I think that a: should not be a slot. Instead 'a' should return a reference object; sending the message ':' to it would update the value.
In the case of 'b' it should also return a slot object as well. Passing '+' to it would not be recognised, and would be overridden in the reference object to return the value b, which would then be passed the message.
The idea is that the compiler/interpreter could optimise this away as the message would typically either match or not match for any given use. (There's a few wrinkles here I'm glossing over, but you should have the basic idea.)
b) 'Allow optional parameters'
For example, Self could have an object called 'Button' that might have optional parameters to set the font, size etc. This can reduce the size of APIs considerably. This allows Self to become a decent scripting language.
c) No lexical scoping
Methods instantiations should inherit off an assignable slot, that is set to point to self. Statements should inherit off method instantiations. This simplifies the language, and regularises it.
(Again the compiler can optimise this away.)
d) Privacy
Some mechanism is needed for this. The minimal implementation is a slot that can only be accessed when called from a method found in a slot of self; but that's a bit cumbersome to code with. (If that sounds completely bizarre, don't worry, there are much better implementations.)
Anyway. What do people think? Good/bad/implementation X does that already...
Ian,
a) 'references'
This is a cute idea which reminds me of the "value holders" ParcPlace introduced into its GUI classes.
If you made the reference object point to its value using an assignable parent slot, then you would have no problems sending references messages that the value is actually supposed to execute. Of course, then you have the "what is the value of self?" problem that is the curse of any proxying scheme.
But how does sending the ":" message to the reference work (in theory, not in the actual implementation which, as you say, can be optimized to be anything we like)? If it is some kind of slot, then we are back where we started. If it is built in somehow then it makes more sense but the language is still irregular since it needs a special case.
In case you missed my own silly proposal for this (which would also handle your b): http://groups.yahoo.com/group/self-interest/message/1066
b) 'Allow optional parameters'
While Self doesn't allow this, you can define a series of methods manually to get a similar effect (this is used in quite a few places, including the scheme to allow optional parameters to blocks). Where this doesn't work too well is when there are too many combinations, usually in initialization methods. Given that assignment slots return the receiver, all of the following should work:
button size: 40@10
button font: f
button color: bc
(button size: 40@10) font: f
(button color: bc) font: f
((button font: f) size: 40@10) color: bc
and so on. Of course, the needed parenthesis ruin it for scripting.
c) No lexical scoping
Methods instantiations should inherit off an assignable slot, that is set to point to self.
They do, though it is actually an argument slot named ":self*". Think of argument slots as "assign once" data slots and it should be more or less what you want.
Statements should inherit off method instantiations. This simplifies the language, and regularises it.
Blocks do that (through an invisible <lexicalParent*> slot). Statements don't exist at the bytecode level, but earlier Selfs had "inner methods" for representing nested expressions in parenthesis and they also are set to inherit from the execution context.
Self didn't have lexical scoping until the latest release - Self 4.0 still faked it with inheritance.
d) Privacy
Some mechanism is needed for this. The minimal implementation is a slot that can only be accessed when called from a method found in a slot of self; but that's a bit cumbersome to code with. (If that sounds completely bizarre, don't worry, there are much better implementations.)
That implementation was used in Self 1 and 2. You would put a "_" in front of a slot name to make it private or a "^" to make it public (putting nothing at all also made it public). For data slots, you could set the visibility of the corresponding assignment slot separately. Normal message sends ignored private slots, but implicit self sends would find them.
This was dropped from the virtual machine in Self 3, but even in the current user interface you can declare the visibility of slots (public slots are shown in bold face, private ones in italics). It is simply documentation now and has no effect on execution.
A more interesting scheme was created for the Us variant of Self (http://citeseer.nj.nec.com/smith96simple.html) although the programming environment would have to help automate the process somewhat. -- Jecel
Jecel Assumpcao Jr wrote:
Ian,
a) 'references'
This is a cute idea which reminds me of the "value holders" ParcPlace introduced into its GUI classes.
If you made the reference object point to its value using an assignable parent slot, then you would have no problems sending references messages that the value is actually supposed to execute. Of course, then you have the "what is the value of self?" problem that is the curse of any proxying scheme.
But how does sending the ":" message to the reference work (in theory, not in the actual implementation which, as you say, can be optimized to be anything we like)? If it is some kind of slot, then we are back where we started. If it is built in somehow then it makes more sense but the language is still irregular since it needs a special case.
All languages need primitives. The point is to get the greatest expressibility from the primitives.
In case you missed my own silly proposal for this (which would also handle your b): http://groups.yahoo.com/group/self-interest/message/1066
b) 'Allow optional parameters'
While Self doesn't allow this, you can define a series of methods manually to get a similar effect (this is used in quite a few places, including the scheme to allow optional parameters to blocks). Where this doesn't work too well is when there are too many combinations, usually in initialization methods. Given that assignment slots return the receiver, all of the following should work:
button size: 40@10 button font: f button color: bc (button size: 40@10) font: f (button color: bc) font: f ((button font: f) size: 40@10) color: bc
and so on. Of course, the needed parenthesis ruin it for scripting.
Yes. More like:
myNewButton: button new: Size: 40@10 Font: f Color: bc
It makes all the difference in readability.
c) No lexical scoping
Methods instantiations should inherit off an assignable slot, that is set to point to self.
They do, though it is actually an argument slot named ":self*". Think of argument slots as "assign once" data slots and it should be more or less what you want.
I didn't know that! That's the way it SHOULD be! ;-)
Statements should inherit off method instantiations. This simplifies the language, and regularises it.
Blocks do that (through an invisible <lexicalParent*> slot). Statements don't exist at the bytecode level, but earlier Selfs had "inner methods" for representing nested expressions in parenthesis and they also are set to inherit from the execution context.
Self didn't have lexical scoping until the latest release - Self 4.0 still faked it with inheritance.
That's odd. Why add lexical scoping?
OK. I clearly don't understand lexical scoping! What's the point of it; how can it be better than inheritance?
d) Privacy
Some mechanism is needed for this. The minimal implementation is a slot that can only be accessed when called from a method found in a slot of self; but that's a bit cumbersome to code with. (If that sounds completely bizarre, don't worry, there are much better implementations.)
That implementation was used in Self 1 and 2. You would put a "_" in front of a slot name to make it private or a "^" to make it public (putting nothing at all also made it public). For data slots, you could set the visibility of the corresponding assignment slot separately. Normal message sends ignored private slots, but implicit self sends would find them.
Not really the same thing. If I inherit off an object I have access to the private slots. That's bad.
This was dropped from the virtual machine in Self 3, but even in the current user interface you can declare the visibility of slots (public slots are shown in bold face, private ones in italics). It is simply documentation now and has no effect on execution.
A more interesting scheme was created for the Us variant of Self (http://citeseer.nj.nec.com/smith96simple.html) although the programming environment would have to help automate the process somewhat.
Hmm. Interesting. I'll check it out.
-- Jecel
On Monday 13 August 2001 17:16, Ian Woollard wrote:
All languages need primitives. The point is to get the greatest expressibility from the primitives.
Indeed, I was just wondering what your ":" brought us that we didn't have with Self's "<-".
((button font: f) size: 40@10) color: bc
and so on. Of course, the needed parenthesis ruin it for scripting.
Yes. More like:
myNewButton: button new: Size: 40@10 Font: f Color: bc
It makes all the difference in readability.
In Smalltalk there is the cascaded messages syntax:
myNewButton <- Button new size: 40@10; font: f; color: bc
and GNU Smalltalk is already pretty good at scripting applications. But prefer to create programs (scripts or not) graphically instead of by typing, so I don't worry about syntax as much.
Self didn't have lexical scoping until the latest release - Self 4.0 still faked it with inheritance.
That's odd. Why add lexical scoping?
Using new special bytecodes for accessing local variables makes an interpreter more practical than with the original self send bytecodes. Self 4.1 is the result of several interesting experiments including such an interpreter.
There are also new branch bytecodes which the parser could use for popular control structures, but I didn't notice this actually being done. Neither of these changes make much of difference for a compiler. And neither required any source code changes at all, so they are transparent to most programmers.
OK. I clearly don't understand lexical scoping! What's the point of it; how can it be better than inheritance?
Lexical scoping is when the text of the program shows you what names are visible at a give point in the source:
sillyExample: arg = ( | temp <- 0 | arg > 7 ifTrue: [ temp: 2 * arg ]. temp + myBase )
Inside the block both "temp" and "arg" are used, though they are not defined locally. But the block is lexically embedded inside a method that does define these names, so it is reasonable for a programmer to expect that they could be used there.
We use "myBase" which is not defined anywhere in the above example. We are supposing that this is defined inside the object that will receive this message or in one of its parents. In Self this works due to the :self* argument slot which makes the receiver a parent of the execution context. In other languages this works because the text of the method is defined inside the text that describes the object, so "myBase" is available in the outer lexical scope.
If you use lexical scoping exclusively to indicate inheritance, then your program must be a text with a very strict nesting structure. This is what Beta is like and it does a good job of it. In Self we like to have objects floating around the screen pointing to each other without one being "inside" the other in any sense. Explicit parent pointers are the way to show what names we are importing into the local context. But this would be awkward for something as small as the block above, so we don't write anything and let Self 4.0 takes care of all the hidden parent pointers. Or Self 4.1 take care of accessing lexically enclosing contexts at runtime. It doesn't make much of a difference in practice.
[private slots in Self 1 and 2]
Not really the same thing. If I inherit off an object I have access to the private slots. That's bad.
But sometimes that is exactly what you want to do. So you would need:
- public slots - private inheritable slots - totally private slots
Otherwise any public method that called a private one would break when invoked by a child object that didn't reimplement the private one.
-- Jecel
Jecel Assumpcao Jr wrote:
On Monday 13 August 2001 17:16, Ian Woollard wrote:
All languages need primitives. The point is to get the greatest expressibility from the primitives.
Indeed, I was just wondering what your ":" brought us that we didn't have with Self's "<-".
Several things-
a) it can be used for privacy- by inheriting the handle object appropriately you can add checking into the object so that it only allows certain objects to talk to it.
b) non builtin syntax can be added to the handle object like:
c++. c += 5.
etc.
Also, reference semantics are useful in their own right.
((button font: f) size: 40@10) color: bc
and so on. Of course, the needed parenthesis ruin it for scripting.
Yes. More like:
myNewButton: button new: Size: 40@10 Font: f Color: bc
It makes all the difference in readability.
In Smalltalk there is the cascaded messages syntax:
myNewButton <- Button new size: 40@10; font: f; color: bc
and GNU Smalltalk is already pretty good at scripting applications. But prefer to create programs (scripts or not) graphically instead of by typing, so I don't worry about syntax as much.
Self didn't have lexical scoping until the latest release - Self 4.0 still faked it with inheritance.
That's odd. Why add lexical scoping?
Using new special bytecodes for accessing local variables makes an interpreter more practical than with the original self send bytecodes. Self 4.1 is the result of several interesting experiments including such an interpreter.
There are also new branch bytecodes which the parser could use for popular control structures, but I didn't notice this actually being done. Neither of these changes make much of difference for a compiler. And neither required any source code changes at all, so they are transparent to most programmers.
OK. I clearly don't understand lexical scoping! What's the point of it; how can it be better than inheritance?
Lexical scoping is when the text of the program shows you what names are visible at a give point in the source:
sillyExample: arg = ( | temp <- 0 | arg > 7 ifTrue: [ temp: 2 * arg ]. temp + myBase )
Inside the block both "temp" and "arg" are used, though they are not defined locally. But the block is lexically embedded inside a method that does define these names, so it is reasonable for a programmer to expect that they could be used there.
We use "myBase" which is not defined anywhere in the above example. We are supposing that this is defined inside the object that will receive this message or in one of its parents. In Self this works due to the :self* argument slot which makes the receiver a parent of the execution context. In other languages this works because the text of the method is defined inside the text that describes the object, so "myBase" is available in the outer lexical scope.
If you use lexical scoping exclusively to indicate inheritance, then your program must be a text with a very strict nesting structure. This is what Beta is like and it does a good job of it. In Self we like to have objects floating around the screen pointing to each other without one being "inside" the other in any sense. Explicit parent pointers are the way to show what names we are importing into the local context. But this would be awkward for something as small as the block above, so we don't write anything and let Self 4.0 takes care of all the hidden parent pointers. Or Self 4.1 take care of accessing lexically enclosing contexts at runtime. It doesn't make much of a difference in practice.
Yeah. Its just sensible- I'd already worked it out I guess, I was just confused by the way it was explained in the User Guide. In a real sense Self doesn't HAVE lexical scoping; and doesn't need it- it's all just syntactic sugar.
[private slots in Self 1 and 2]
Not really the same thing. If I inherit off an object I have access to the private slots. That's bad.
But sometimes that is exactly what you want to do. So you would need:
- public slots
- private inheritable slots
- totally private slots
Not clear why I would want private inheritable slots. Inheriting up might be ok, but inheriting down; never. If I really want that then I can add a separate public slot that can work out whether an object is worthy of accessing the private slots.
Otherwise any public method that called a private one would break when invoked by a child object that didn't reimplement the private one.
That could be allowed I think- only when 'self' contains the private slot, and the sender was self is it allowed. Something like that.
-- Jecel
Better late than never, I suppose...
On Tuesday 14 August 2001 20:47, Ian Woollard wrote:
Indeed, I was just wondering what your ":" brought us that we didn't have with Self's "<-".
Several things-
a) it can be used for privacy- by inheriting the handle object appropriately you can add checking into the object so that it only allows certain objects to talk to it.
Right.
b) non builtin syntax can be added to the handle object like:
c++. c += 5.
etc.
Also, reference semantics are useful in their own right.
Hmmmm... this seems to me like the opposite of a). Here you are exposing things that would be better handled internally. Don't forget that Alan Kay started OOP (sorry Simula fans!) because he felt assignments were too low level to capture what was going on in the computer.
Yeah. Its just sensible- I'd already worked it out I guess, I was just confused by the way it was explained in the User Guide. In a real sense Self doesn't HAVE lexical scoping; and doesn't need it- it's all just syntactic sugar.
True - the "contexts are cloned method objects" story feels much closer to the dynamic scoping used in older Lisps. Most people can't tell the difference, though this seems to be causing a lot of problems in newer languages like Python.
But sometimes that is exactly what you want to do. So you would need:
- public slots
- private inheritable slots
- totally private slots
Not clear why I would want private inheritable slots. Inheriting up might be ok, but inheriting down; never. If I really want that then I can add a separate public slot that can work out whether an object is worthy of accessing the private slots.
I am not sure what you mean by "up" and "down".
Is "up" when object A runs method X actually found in parent B and X sends a self message Y that is a private method in A?
And is "down" when object A runs local method X and sends a self message Y that is a private method in parent B?
Neither seems absolutely evil to me, though I would like control over these situations.
Otherwise any public method that called a private one would break when invoked by a child object that didn't reimplement the private one.
That could be allowed I think- only when 'self' contains the private slot, and the sender was self is it allowed. Something like that.
In my example 'self' didn't contain the private slot - its parent did. It is a third case, not either down nor up from above (though it is pretty close to "down").
-- Jecel
Jecel Assumpcao Jr wrote:
Better late than never, I suppose...
On Tuesday 14 August 2001 20:47, Ian Woollard wrote:
Indeed, I was just wondering what your ":" brought us that we didn't have with Self's "<-".
Several things-
a) it can be used for privacy- by inheriting the handle object appropriately you can add checking into the object so that it only allows certain objects to talk to it.
Right.
b) non builtin syntax can be added to the handle object like:
c++. c += 5.
etc.
Also, reference semantics are useful in their own right.
Hmmmm... this seems to me like the opposite of a). Here you are exposing things that would be better handled internally. Don't forget that Alan Kay started OOP (sorry Simula fans!) because he felt assignments were too low level to capture what was going on in the computer.
Yeah. Its just sensible- I'd already worked it out I guess, I was just confused by the way it was explained in the User Guide. In a real sense Self doesn't HAVE lexical scoping; and doesn't need it- it's all just syntactic sugar.
True - the "contexts are cloned method objects" story feels much closer to the dynamic scoping used in older Lisps. Most people can't tell the difference, though this seems to be causing a lot of problems in newer languages like Python.
But sometimes that is exactly what you want to do. So you would need:
- public slots
- private inheritable slots
- totally private slots
Not clear why I would want private inheritable slots. Inheriting up might be ok, but inheriting down; never. If I really want that then I can add a separate public slot that can work out whether an object is worthy of accessing the private slots.
I am not sure what you mean by "up" and "down".
Is "up" when object A runs method X actually found in parent B and X sends a self message Y that is a private method in A?
Uh. No that's down as the private method is found down the inheritance to A. The private method bit is the one to keep your eye on. Inheritance trees always go up to the root unlike wooden trees. This operation is calling down the inheritance tree. That's a safe operation in fact, as object A has the choice of whether X is its parent.
And is "down" when object A runs local method X and sends a self message Y that is a private method in parent B?
Uh. No, that's up. That's the dangerous one. Any evil object can make any other object their parent, and hence access the private slots if the language allows that; and it should not be allow it.
Neither seems absolutely evil to me, though I would like control over these situations.
I think we can assume that no object makes an object it doesn't trust its parent.
Otherwise any public method that called a private one would break when invoked by a child object that didn't reimplement the private one.
That could be allowed I think- only when 'self' contains the private slot, and the sender was self is it allowed. Something like that.
In my example 'self' didn't contain the private slot - its parent did. It is a third case, not either down nor up from above (though it is pretty close to "down").
Anyway, that operation is safe, because the method that accesses the private slot is in the same object as the private one; so it should be allowed.
Let me define the algorithm. First, it's probably necessary to define my terms.
Theres: self receiver (the object being sent a new message) receive_slot_holder (the object that contains the slot).
Private slots should be accessed iff.: (self == receiver) and (receive_slot_holder == self)
The first part says that only the object can access a private slot inherited from itself. (No fiddling with friends private parts as Bjarne Stroustrup puts it.)
The second says that the private slot must be not inherited off of, but instead be part of the object itself. That stops children fiddling with parents privates.
Anyway I think I've got this straight/comments welcome.
-- Jecel
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
On Thursday 23 August 2001 13:47, Ian Woollard wrote:
[up and down, the real story]
Ok. While the arrow goes from the object to its parent, I like to draw the tree with the parent higher up.
I think we can assume that no object makes an object it doesn't trust its parent.
Agreed.
Let me define the algorithm. First, it's probably necessary to define my terms.
Theres: self receiver (the object being sent a new message) receive_slot_holder (the object that contains the slot).
Private slots should be accessed iff.: (self == receiver) and (receive_slot_holder == self)
The first part says that only the object can access a private slot inherited from itself. (No fiddling with friends private parts as Bjarne Stroustrup puts it.)
This is what Self 1 and 2 had. Actually, I think that if an object sent a message to itself with a normal message send ("self someMess") instead of with an implicit self message (just "someMess") then it would not see the private slots. But this is not the kind of detail that anyone is likely to notice.
The second says that the private slot must be not inherited off of, but instead be part of the object itself. That stops children fiddling with parents privates.
Self never had this, but an interesting way could be to allow local methods for method objects. Something like
.... myMethod = ( | x <- 0. localMeth: z = (x+z) | .... ). ...
This is sort of like a named block. There would be an advantage over a regular block if localMeth: was used several times inside myMethod. Now lets move this to some parent object:
... myMethod = ( | x <- 0. private* = ( | localMeth: z = (x+z) | ) | .... ). ....
Should work just like before. But if we could share this private object with other methods in this object, the this set of methods could invoke localMeth: but nobody from the outside (including children) could.
This can't be done textually without putting the private object in some globally known place (which would defeat the purpose, of couse!) but this could be built in the graphical environment. All we need to do is modify the VM to support it ;-)
I just tried it and the graphical environment prints the above method as "( | x <- 0. private* = 0 _AsObject | ...". So it was easy to create a second method with the same private parent. It doesn't run because the parent of the localMeth: is the receiver of myMethod and not the current context like it would have been with a block (Self is ignoring our attempt at "nesting"). So localMeth: doesn't find "x".
It's important to expose the right things. If I want to pass an objects variables to an editor for editing then I should be allowed to. I should not have to jump through hoops to give something that the language can give; I've had to jump the hoops in Java- no references (lvalues in C speak) are a serious restriction in the expressibility of the language. There are ways; but they are not good; I've found.
Well, there is "domain level programming" and then there is "meta programming". You should have to jump through some hoops to switch from one to the other, otherwise the danger of getting them mixed up is too great.
Elegance of code is important.
how would you do:
newIntegerBrowser: &myIntSlot.
in Self?
This would work if there is such a thing as an "integer slot browser":
newIntegerBrowser: (reflect: self) at: 'myIntSlot'
I would hate to see something like this in a "regular" application. -- Jecel
Jecel Assumpcao Jr wrote:
Better late than never, I suppose...
On Tuesday 14 August 2001 20:47, Ian Woollard wrote:
Indeed, I was just wondering what your ":" brought us that we didn't have with Self's "<-".
Several things-
a) it can be used for privacy- by inheriting the handle object appropriately you can add checking into the object so that it only allows certain objects to talk to it.
Right.
b) non builtin syntax can be added to the handle object like:
c++. c += 5.
etc.
Also, reference semantics are useful in their own right.
Hmmmm... this seems to me like the opposite of a).
Brevity in a language is a very good thing. There is a good correlation between number of lines of code per week that engineers can write.
Here you are exposing things that would be better handled internally.
It's important to expose the right things. If I want to pass an objects variables to an editor for editing then I should be allowed to. I should not have to jump through hoops to give something that the language can give; I've had to jump the hoops in Java- no references (lvalues in C speak) are a serious restriction in the expressibility of the language. There are ways; but they are not good; I've found.
Elegance of code is important.
how would you do:
newIntegerBrowser: &myIntSlot.
in Self?
-- Jecel
self-interest@lists.selflanguage.org