Jecel Assumpcao Jr writes:
(note - since I don't know how to set up the SELF_WORKING_DIR variable the droplet didn't work)
You have a few options here.
One is to hack. :) You can change the Applescript to point directly to Self. Open the SelfDroplet project in Project Builder (you might be able to do this in the Applescript Script Editor too) and change:
""${SELF_WORKING_DIR}"/vm/mac_osx/vm_project/build/Self.app/Contents/MacOS/Self -s "" & unixFileName & """
to e.g., :
"Projects/self/vm/mac_osx/vm_project/build/Self.app/Contents/MacOS/Self -s "" & unixFileName & """
Another is to set the SELF_WORKING_DIR in your .cshrc.
A third, probably the most "MacOS X" way is to set this in your ~/.MacOSX/environment.plist . See http://developer.apple.com/qa/qa2001/qa1067.html for details.
- sekhar
-- C. Ramakrishnan cramakrishnan@acm.org
On Sunday 12 May 2002 23:41, C. Ramakrishnan wrote:
[three ways to fix the Self droplet]
Thanks for the explanation! Though I know both the old Mac OS and Unix very well, I have had my share of complications with Mac OS X.
The droplet looks like a shell batch file, so I suppose we could test SELF_WORKING_DIR and if not set guess the right value with something like
pwd | awk -F / -- '{NF=NF-1; print $0}' | tr " " /
supposing that the droplet is in the objects subdirectory and we want the name of its parent?
-- Jecel
Hi,
I have another question. When you have an object, like the dictionary in globals with a slot like values (a vector) in this global, then normally not the global vector is in this slot, it is a copy of this global vector. So when you make then a copy of this dictionary, a more or less deep copy is made (values and keys are copied too). In my understanding it would be much better to use the global vector because then all the chances that I would make for the vector (new data slots) were immediately available. I know normally nobody has to chance a system object but the problem is the same for other "complex" objects.
So I ask me what is the reason for that:
- it is not the excepted behaviour that changes on the global vector have any impact on the one that is used in the dictionary - but behaviour changes have still an impact because they are made in the parent of this vector.
- when somebody is changing this vector (by inspecting and manipulating it) the global one would be changed. This would be a good reason, but it seams to me, that for this problem this is not the right solution. If we want to prevent this it would be much better to make global objects more or less "unchangeable".
So why don't we use for slots in prototypes the excepted prototype instead of a copy of them?
Thorsten Dittmar
On Wednesday 15 May 2002 11:40, Thorsten Dittmar wrote:
I have another question. When you have an object, like the dictionary in globals with a slot like values (a vector) in this global, then normally not the global vector is in this slot, it is a copy of this global vector. So when you make then a copy of this dictionary, a more or less deep copy is made (values and keys are copied too). In my understanding it would be much better to use the global vector because then all the chances that I would make for the vector (new data slots) were immediately available. I know normally nobody has to chance a system object but the problem is the same for other "complex" objects.
This is a case of "defensive programming". For example:
sillyMethod: n = ( | temp <- list copyRemoveAll | temp: temp copyRemoveAll. n timesDo: [ temp add: '*' ]. temp )
This would work just as well (and would be more readable) if the declaration of the temp slot had been just
temp <- list
There is no reason not to use the global 'list' as the value in the method prototype object since the first thing we are going to do anyway is to copy it. And there is no good reason to use 'copyRemoveAll' instead of a plain 'copy' since we know that the prototype list is empty.
Unless, of course, there is a chance that either I or somebody else later editing this method can make a mistake. That is what defensive programming is all about - lots of silly tests that don't make sense if everything was built as it should.
So I ask me what is the reason for that:
- it is not the excepted behaviour that changes on the global vector
have any impact on the one that is used in the dictionary - but behaviour changes have still an impact because they are made in the parent of this vector.
Yes, such changes might break my code. This is where a viewpoint system like in Us might be nice - we could more easily say what we need to remain unchanged and what we would like to have the latest version of. Right now the way to do this would be to add an empty parent object to our cloned vector and to copy into it any slots that must remain as they are. Then we will be protected against random changes to 'traits vector' and yet still automatically use any bug fixes or improvement that don't break our dictionary object.
- when somebody is changing this vector (by inspecting and
manipulating it) the global one would be changed. This would be a good reason, but it seams to me, that for this problem this is not the right solution. If we want to prevent this it would be much better to make global objects more or less "unchangeable".
In a multiuser Self we could have global objects be unchangeable except by people with special permissions. That would avoid most accidents yet allow needed changes to be made.
So why don't we use for slots in prototypes the excepted prototype instead of a copy of them?
Just to reduce the effects of mistakes (like somebody forgetting to override the 'copy' method to do a deep copy, as you mentioned in the beginning).
-- Jecel
Hi,
I'm not sure if I do some thing wrong or if it is a bug.
Here the situation: I have a module with 2 prototypes (b and c). Prototype b has a slot named x. I use the annotation mechanism to initialize x as a copy of c. So far so good... Now file out the module and use bootstrap read: 'filename' From: 'dictionary' to file it in.
I will get an error because x can not be initialized because the prototype c is not installed at all at this moment.
Maybe I misused the annotation mechanism. Of course I know a workaround, but I want to know what the right way for the not so uncommon situation is.
Can anybody help me?
Thorsten Dittmar
On Wednesday 05 June 2002 15:03, Thorsten Dittmar wrote:
I'm not sure if I do some thing wrong or if it is a bug.
It is a bug.
Here the situation: I have a module with 2 prototypes (b and c). Prototype b has a slot named x. I use the annotation mechanism to initialize x as a copy of c. So far so good... Now file out the module and use bootstrap read: 'filename' From: 'dictionary' to file it in.
I will get an error because x can not be initialized because the prototype c is not installed at all at this moment.
The problem is that the module created the objects in the wrong order. I tried this and got the same error as you did, and then I did it again but with the objects named "f" and "e" instead of "b" and "c" and it worked as it should.
If you look at the object "transporter fileOut slotSorter" (specially in the method 'topoSortFrom:Path:IfCycle:') you will see that though it starts out with a simple alphabetical order for the top slots in a module, it makes a great effort to find all dependencies and reorder things to fix them. Unfortunately it doesn't seem to handle this (rather common) case.
Maybe I misused the annotation mechanism. Of course I know a workaround, but I want to know what the right way for the not so uncommon situation is.
One workaround is to choose top level names so things fileOut in the right order. To get back a module that would otherwise be lost you might want to edit the .self in a text editor, though this is a lot of work and the problem will reappear the next time you save the module.
I ran into this myself when working in tinySelf 1 back in 1997, which was one of the reasons I never finished debugging it. On the other hand, I wrote several other applications and my trainees did too and none of them had this problem. It depends on luck (what your objects are named) and style (whether you use "InitializeTo:" annotations or not).
-- Jecel
Hi Jecel,
you can not solve this problem by ordering, or let me say you can easily construct a case in which ordering does not work anymore. I think it is a real hard task to move objects from one system to another via plain text. I remember me that back in the good old days Servio had a solution for that. I was one of the goodies from their consulting group. I will try to find that old media and will hope that it is still readable. If I will find something useful I will send it to the list.
Do you use initializeTo: or don't you? When I remember me correctly, in Smalltalk all the construction stuff (create classes and methods) will be done first and all the initialize methods are called after that. I can try to implement this, but when initializeTo: is just a relict from the old days then it makes no sense to spend some effort.
On 6/6/02 5:44 PM, "Jecel Assumpcao Jr" jecel@merlintec.com wrote:
On Wednesday 05 June 2002 15:03, Thorsten Dittmar wrote:
I'm not sure if I do some thing wrong or if it is a bug.
It is a bug.
Here the situation: I have a module with 2 prototypes (b and c). Prototype b has a slot named x. I use the annotation mechanism to initialize x as a copy of c. So far so good... Now file out the module and use bootstrap read: 'filename' From: 'dictionary' to file it in.
I will get an error because x can not be initialized because the prototype c is not installed at all at this moment.
The problem is that the module created the objects in the wrong order. I tried this and got the same error as you did, and then I did it again but with the objects named "f" and "e" instead of "b" and "c" and it worked as it should.
If you look at the object "transporter fileOut slotSorter" (specially in the method 'topoSortFrom:Path:IfCycle:') you will see that though it starts out with a simple alphabetical order for the top slots in a module, it makes a great effort to find all dependencies and reorder things to fix them. Unfortunately it doesn't seem to handle this (rather common) case.
Maybe I misused the annotation mechanism. Of course I know a workaround, but I want to know what the right way for the not so uncommon situation is.
One workaround is to choose top level names so things fileOut in the right order. To get back a module that would otherwise be lost you might want to edit the .self in a text editor, though this is a lot of work and the problem will reappear the next time you save the module.
I ran into this myself when working in tinySelf 1 back in 1997, which was one of the reasons I never finished debugging it. On the other hand, I wrote several other applications and my trainees did too and none of them had this problem. It depends on luck (what your objects are named) and style (whether you use "InitializeTo:" annotations or not).
-- Jecel
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
Thorsten,
you can not solve this problem by ordering, or let me say you can easily construct a case in which ordering does not work anymore. I think it is a real hard task to move objects from one system to another via plain text.
This is very true, but the Transporter moves slots and not objects between systems. So it could find a proper order even for the case of circular references. Imagine that you add a slot "y" to "c" pointing to "b". We could have a file like this:
- add a slot "b" to "globals" - add some slots to "b" - add a slot "c" to "globals" - add some slots to "c" - add a slot "y" to "c" and initialize to "b" (or "b copy") - add a slot "x" to "b" and initialize to "c" (or "c copy")
Hmmm... interesting.... I just tried your example again but initializing the "x" slot to "c" instead of "c copy" and the Transporter did the right thing.
Now I added the "y" slot as mentioned above and it still worked perfectly. So it is the "copy" that is giving the Transporter a hard time.
I remember me that back in the good old days Servio had a solution for that. I was one of the goodies from their consulting group. I will try to find that old media and will hope that it is still readable. If I will find something useful I will send it to the list.
For binary formats this is easy to solve even if you deal with whole objects instead of slots.
Do you use initializeTo: or don't you? When I remember me correctly, in Smalltalk all the construction stuff (create classes and methods) will be done first and all the initialize methods are called after that. I can try to implement this, but when initializeTo: is just a relict from the old days then it makes no sense to spend some effort.
Slot initializers in Self aren't quite the same thing as initialization methods in Smalltalk.
Perhaps you should reconsider using "copy" in your object descriptions. Since what is saved in the text description is the prototypes that are not meant to be used directly, it should make no difference in practice. Instead of using "b" directly, you will always ask for a copy of it. And inside b's 'copy' method will be code to copy the contents of its "x" slot (or else all b clones will share this value and that is probably not what you want). Right now b clones will start with a copy of a copy of "c" in their "x" slots. If you eliminate "copy" from the initialization code, then they will start out with a copy of "c" instead. If you want future changes to "c" to have an effect on future clones of "b", then this will do it (but your current code will not).
-- Jecel
If you look into the globals.transporter.fileOut there are 2 methods that would maybe help.
fileOutSiteEffects that method put a script on the end of the file out which can be used to do all the initializes.
fileOutAInitializerOf: c that one creates the initializer string.
So it would be "easy" to modify the transporter in that way. So the problem with undefined prototypes in initialize statements would be fixed, but then you can still have a problem with a defined but uninitialized prototype. So when the initialize sequence for that prototype will starts it was already copied during the initialize sequence of the other prototype.
And then we are back to the point that sorting or ordering will not help in all cases. The only thing that will help here, is prototypes who refer prototypes instead of copies... you remember that discussion about defensive programming ;-)
Does anybody know something like BOSS in VisualWorks for Self?
Thorsten
On Thursday 06 June 2002 17:15, Thorsten wrote:
If you look into the globals.transporter.fileOut there are 2 methods that would maybe help.
fileOutSiteEffects that method put a script on the end of the file out which can be used to do all the initializes.
Ok, though I think the idea is to put stuff in the module's postFileIn method that can't be handled in any other way, like reading setup information from the OS for later use.
fileOutAInitializerOf: c that one creates the initializer string.
So it would be "easy" to modify the transporter in that way.
The modification should be in the method that I mentioned before so that not only a direct reference to an object but also the use of an object in an expression would be considered an ordering constraint.
So the problem with undefined prototypes in initialize statements would be fixed, but then you can still have a problem with a defined but uninitialized prototype. So when the initialize sequence for that prototype will starts it was already copied during the initialize sequence of the other prototype.
You will have the same problem if you change things after loading the module in any case.
And then we are back to the point that sorting or ordering will not help in all cases. The only thing that will help here, is prototypes who refer prototypes instead of copies... you remember that discussion about defensive programming ;-)
Yes, there is a contradiction between extra safe coding and having changes be reflected in all interesting places. You can't have both. This design problem also appears when you have to decide whether to put a slot in the object itself, in a parent or in an even higher up ancestor. No matter what choice you make, there will be some future situation in which it will turn out to have been the wrong choice.
Unfortunately the Transporter forces a certain programming style, so the choice is already made for you if you need to use it.
Does anybody know something like BOSS in VisualWorks for Self?
Try "transporter binaryBeamOut scanApplication:" and "transporter binaryBeamIn readApplication:" and see if they don't what you need.
I have been doing some statistics to see what I can do about binary modules and so far have had some surprising results. It seems that "enumerating all" spreads the objects around much more than I would expect. Checking out how "far" references are (where 0 would mean self, 1 the next object in enumerating all order, -1 the previous object, 2 the object after the next one and so on...) I got that most references would need between 14 and 18 bits with a far smaller peak at 7 and 8 bits. That is almost the worst possible order. I suspect that if we divided the objects into modules things would look very different.
It takes the Ultra several hours to go through all the objects, so it will be a while before I have all the results I need. One option would be to add memory, but I am thinking of running benchmarks on the Sparc, the iBook and some PCs to confirm that this is the best alternative. I am not sure a PC with 6 times the clock would compensate for the lack of the SIC. A quick test on the iBook showed that I'll have to do some debugging to run the benchmarks (getnodename seems to be broken on the Mac OS X, for example).
BTW, thanks for the Droplet tip in the other email.
-- Jecel
Hi
Yes, there is a contradiction between extra safe coding and having changes be reflected in all interesting places. You can't have both.
Hmmm I no what you mean, but this argumentation is a little to simple. There are a lot of different options to make things more save or to support defensive programming, of course they have other demerits. Just as an example, it would be possible to have a boolean annotation for prototypes that indicates if a prototype can be modified or not. Or as in Gemstone, where you have a login and only as the super user you can change some objects, etc.
How ever, it is like it is and the people who did it had good reasons for that, that for sure. A change at this part would be really hard and there are enough things that are more valuable for the community and even easier to do.
This design problem also appears when you have to decide whether to put a slot in the object itself, in a parent or in an even higher up ancestor. No matter what choice you make, there will be some future situation in which it will turn out to have been the wrong choice.
Oh yes! And even future can come very fast ;-) That is the reason why I love systems with good do/undo functionalities and with a good versioning system. Jecel, don't you have a student who wants to implement do/undo/redo stuff in the editors ;-)
Thorsten
Thorsten and Jecel,
I have just read a recent thread about the transporter failing to order things properly. As the author of the transporter, I can tell you what I was thinking:
The general problem of finding an ordering by trying to understand what code does (in this case an initialization message) turned out to be beyond me. So, when you use the transporter, you have to try reading your modules in to a clean world in order to see if it got it right. It tries but sometimes fails. When it fails, you have to reorder your modules in the submodule hierarchy (or make module B a submodule of A) in order to get it to work.
This is drawback of using the transporter, but one I do not know how to reliably solve. I do personally enjoy the development style that emerges from using it. I work for about a week with snapshots, then file everything out when I reach a good stable point. Then I file in to a clean snapshot, test, do a cvs commit, and then build a new snapshot from scratch.
Hope this helps,
- Dave
PS: I bet that I won't be able to post this to the yahoo group. If you don't see it there, it would be great if you could repost it for me.
On Tuesday 11 June 2002 04:03, Thorsten Dittmar wrote:
[permissions for safe programming instead of extra copying] How ever, it is like it is and the people who did it had good reasons for that, that for sure. A change at this part would be really hard and there are enough things that are more valuable for the community and even easier to do.
Self is an experimental system and has only been used by very experienced people who know how to avoid the pitfalls. Your suggestions would make the system more useful to "regular" programmers.
Oh yes! And even future can come very fast ;-) That is the reason why I love systems with good do/undo functionalities and with a good versioning system. Jecel, don't you have a student who wants to implement do/undo/redo stuff in the editors ;-)
You would need to make modifications everywhere. Select "change colors" for a complex morph and it is very likely that you will be surprised with the result and might not like it. Unless you saved a snapshot just before doing this it is very, very hard to get things back the way they were.
The solution I prefer is to handle this in the virtual memory system instead of at the application level. And this should be done in a way that includes your suggestions about defensive programming.
On Wed, 12 Jun 2002 17:21:37 -0700 David Ungar wrote:
The general problem of finding an ordering by trying to understand what code does (in this case an initialization message) turned out to be beyond me.
There is no solution (that code might have a 'perform:' or something equally opaque). But I just had the idea that if you compile the initialization code, any object included as a literal should be created before the current slot. This would handle Thorsten's case as well as the current global object initializers.
Note that we were discussing the problem of slot order within a single module, not between modules.
PS: I bet that I won't be able to post this to the yahoo group.
It worked.
-- Jecel
For my current work I need a testing framework, so I start porting sUnit to self. For the first step it is only necessary to have something like sUnit on self, so I stopped all my though about the exact meaning of a unit for self. That can be done in the next step.
sUnit is heavily based on exception handling. It try to find out, if there is something similar to this in self and as far as I can say now, it is not. Of course it would be easily possible to implement something like this, but a system change like this was not my intention.
So as an alternative I looked at the process model and found it useful for the requirements of sUnit: (send one test message after another to an object, catch all errors and count the errors, warning and successful calls)
My current implementation idea will fork a process for every call and if the causeOfDeath = 'terminated' it was a successful run.
I want to change the implementation of warning: string a little bit. In the moment it looks into the preferences and when the slot noisy is true it make a beep and wrote a string to stOut. I want to add a list warning to a subprototype of process to collect all warnings.
So if causeOfDeath is something else then 'terminated' and causeOfError is not ok anymore it would be an error.
So far so good. My problem is to make sure that "no" error will pop up a debugger or so. My current idea is the implement a special version of the mixin schedulerCalls. Normally when an error occurs the process start a communication sequence with the scheduler to create a debugger
suspendAndTrace: error causeOfError: error. performInScheduler: suspendAndTraceAction IfInside: [scheduler suspendAndTrace: self]. self
performInScheduler: IfInside: is implemented in this mixin as:
scheduler inScheduler ifTrue: blk False: [_Yield: action]
Using dynamic inheritance it can be exchanged by
scheduler inScheduler ifTrue: [scheduler abort: self] False: [_Yield: abortAction]
What do you think about this. Do you know a easier solution?
Thorsten Dittmar
On Monday 10 June 2002 05:03, Thorsten Dittmar wrote:
sUnit is heavily based on exception handling. It try to find out, if there is something similar to this in self and as far as I can say now, it is not.
There is no exception mechanism in Self, unfortunately, but at least some applications that need it can make use of the the 'onNonLocalReturn:' and 'onReturn:' methods in traits block. Your case is far more complex.
Of course it would be easily possible to implement something like this, but a system change like this was not my intention.
There is enough reflection in Self that it might be possible to do this without any VM changes, but it would probably be easier with help from the VM. In any case it might be a tricky project.
So as an alternative I looked at the process model and found it useful for the requirements of sUnit: (send one test message after another to an object, catch all errors and count the errors, warning and successful calls)
That is how the user interface does things.
[...] Using dynamic inheritance it can be exchanged by
scheduler inScheduler ifTrue: [scheduler abort: self] False: [_Yield: abortAction]
What do you think about this. Do you know a easier solution?
This seems very reasonable. But since you know that you want this when creating the process it might be better to create a new process prototype, copy this method there and change it as you indicated it and then use this new prototype whenever creating a new process for a test.
Changing things to use dynamic inheritance might make things too slow.
-- Jecel
On 6/13/02 11:55 PM, "Jecel Assumpcao Jr" jecel@merlintec.com wrote:
This seems very reasonable. But since you know that you want this when creating the process it might be better to create a new process prototype, copy this method there and change it as you indicated it and then use this new prototype whenever creating a new process for a test.
This way does not work at all. Try it out, make a unchanged new prototype of process.
Then use
| p | p: selfUnitProcess copySend: message copy receiver: 1 Selector: even. p resume waitForSuspension.
You will always run into a vm error.
So I looked closer to:
some applications that need it can make use of the the 'onNonLocalReturn:' and 'onReturn:' methods in traits block.
Please correct me when I'm wrong, my understanding of 'onNonLocalReturn:' is, that when at any time during the evaluation of the receiver block an error occurs the evaluation will be aborted and the argument block will be evaluated. Is this correct?
[1/0] onNonLocalReturn: ['nice try']
Brings up a debugger, not a string outliner.
On Sunday 16 June 2002 04:39, Thorsten Dittmar wrote:
On 6/13/02 11:55 PM, "Jecel Assumpcao Jr" jecel@merlintec.com wrote:
[make new process prototype]
This way does not work at all. Try it out, make a unchanged new prototype of process.
I tried it in Self 4.1.5 in Sparc and it seemed to work, so I tried it again in 4.1.2alpha in Linux and again got the expected results. Note that I wouldn't have been surprised if the VM had crashed as I had never actually tried it before.
I started out by adding this slots to 'globals':
selfUnitProcess = process _Clone
I decided to use the raw primitive instead of risking any of the 'copy' method variations and that might have made a difference.
Then use
| p |
p: selfUnitProcess copySend: message copy receiver: 1 Selector: even. p resume waitForSuspension.
You will always run into a vm error.
It should have been 'even' above or you will be sending the even message to 'shell' (depending on where you typed this) and the result of that will be the selector in the new process.
First I executed the above code but with 'process' instead of 'selfUnitProcess' just to see what I would get. Then I tried again with 'selfUnitProcess' and the result looked exactly the same. Then I added this slot to selfUnitProcess:
errList <- list copyRemoveAll
and the name of selfUnitProcess was changed from "a process (ready process: scheduler" to "an object (ready process: scheduler)". Executing your expression a third time resulted in a new object that also had an 'errList' slot (sharing a value with selfUnitProcess, which is wrong. The 'copy' methods would have to be changed to correct this).
So I looked closer to:
some applications that need it can make use of the the 'onNonLocalReturn:' and 'onReturn:' methods in traits block.
Please correct me when I'm wrong, my understanding of 'onNonLocalReturn:' is, that when at any time during the evaluation of the receiver block an error occurs the evaluation will be aborted and the argument block will be evaluated. Is this correct?
No, it just intercepts attempts to to a non local return from the block. This can be used in some cases where you would use "ensure:" in a system with exceptions, but won't handle any errors.
[1/0] onNonLocalReturn: ['nice try']
Brings up a debugger, not a string outliner.
I did mention that it wouldn't help you ;-)
These examples are more interesting:
[ 1 ] onNonLocalReturn: [ 'nice try' ]
[ ^ 1 ] onNonLocalReturn: [ 'nice try' ]
The first will return 1 and the second 'nice try'. This can be used to ensure that a file is closed even if a nonLocalReturn attempts to unwind the code past the point where the file was opened. I shouldn't have made such an unclear comment, sorry.
-- Jecel
On Sunday 16 June 2002 04:39, Thorsten Dittmar wrote:
On 6/13/02 11:55 PM, "Jecel Assumpcao Jr" jecel@merlintec.com wrote:
[make new process prototype]
This way does not work at all. Try it out, make a unchanged new prototype of process.
I tried it in Self 4.1.5 in Sparc and it seemed to work, so I tried it again in 4.1.2alpha in Linux and again got the expected results. Note that I wouldn't have been surprised if the VM had crashed as I had never actually tried it before.
I started out by adding this slots to 'globals':
selfUnitProcess = process _Clone
I decided to use the raw primitive instead of risking any of the 'copy' method variations and that might have made a difference.
With _Clone it works also at my machine. Thanks for the hint.I checked the copy methods from process (and above), I don't find the reason for that behavior. How ever it works and that is enough for now. Thorsten
Hi,
Just one hint
"Projects/self/vm/mac_osx/vm_project/build/Self.app/Contents/MacOS/Self -s "" & unixFileName & """
If you use this solution the transporter will not work correctly, because during the fileout the variable will be used.
Thorsten
self-interest@lists.selflanguage.org