On Dec 28, 2010, at 11:59 AM, Jecel Assumpcao Jr. wrote:
David Ungar wrote on Mon, 27 Dec 2010 18:50:51 -0800
On Dec 27, 2010, at 11:03 AM, Jecel Assumpcao Jr. wrote:
[gripe - strings instead of objects for annotations]
The string part was an easy implementation way to save space. Sure storing objects would have been nicer.In fact they were changed back and forth to objects in a way that was transparent. Annotations were meant to be functional, so you could replace them but not change them in place.That saves a lot of space. Remember that every slot in every point object conceptually had its own annotation.
Yeah, I didn't find a good alternative that would be reasonably space efficient. In the syntax, an annotation can span a number of slots. But in my designs I ended up just replicating the annotation for each slot separately (sharing when possible). Hmmmm.... just now I see that there is no reason to insist that maps be "flat". Just because the old object tree was replaced by annotation doesn't mean that the maps couldn't remain a tree. Then an annotation that only appears once in the source would only need to appear once in the runtime structure. Lookup performance is not as important in a compiler.
yes
[gripe - strings in the parameters of fail blocks]
Not sure what you mean here about Ole's extractor.
It was easy for unrelated parts of the system to leak into the snapshot that was being extracted. One example is floating point for the factorial - there is one branch in the "power" function where the result could be a float (but it never is in this application) and so you get this huge overhead. The other place is string functionality: the factorial app just needs enough to print its results but due to code like
If the result could be a float, then there isn't much hope for an automatic extractor to know it couldn't be, unless I don't understand your example.
- a = ( |
| asSmallInteger _IntAdd: a IfFail: [| :error. :name. | ('badTypeError' == error) ifTrue: [ " use double dispatching " a addSmallInteger: asSmallInteger ] False: [ ('overflowError' == error) ifTrue: [ " retry after coercing to bigInts " asBigInteger + a asBigInteger ] False: [ primitiveFailedError: error Name: name ]]]).
you bring in stuff from String. Hmmmm..... this isn't nearly as bad as I remember since only '==' is being used. I thought there were lots of 'beginsWith:' all over the place.
At one point there may have been beginsWith:, and you could argue (and I would agree) that we would have been better off making error objects to pass in to fail blocks. The issue there is *not* exceptions vs fail blocks, but rather fail-blocks with string arguments vs fail-blocks with object arguments.
IMNSO, exceptions were a botch invented in languages without blocks .I never liked the fact that control could invisibly branch off.For instance, in a long method with exceptions, containing A; B; C;, you cannot count on B being executed if A is executed. At least with fail blocks you can see the branch in the return symbol in the block .I know that others like exceptions. To me they are like iterators, both were workarounds for languages without blocksthat became enshrined in our culture. I prefer blocks. But that's just me.
Actually, besides blocks you need non local returns to get the effect of exceptions and I think Self code is a great example of how to do this. But when I mentioned "exception objects instead of strings" I was just thinking of having something like
Yes, of course. To me, "blocks" means blocks + block arguments + block locals + (what squeakers call closure semantics) + non-local return. Sorry if that wasn't clear.
traits errors = (| isBadType = false. isOverflow = false. ..... |). badTypeError = (| parent* = traits errors. isBadType = true |). overflowError = (| parent* = traits errors. isOverflow = true |).
This wouldn't really help in the '+' method above very much, but with a more complete hierarchy and some actual functionality in the error objects rather than them being simple tokens it could make a nice difference.
Yup, these would be better to pass in to the fail blocks than strings.
[tinySelf 0: special constant slot for object annotation]
That would allow object annotations to be accessed without reflection, a change from what I considered tobe the appropriate base-level semantics. It is simpler, though. It violates my preferred model of object encapsulation.
I agree, but in tinySelf 0 I had a bunch of "system slots" which you couldn't access by sending messages to their object but which showed up when talking to a mirror. The annotation slot was the only one I added - the rest (like the "hidden parent slots" in the block clones and block activations) were already part of the execution story for Self and I just reified them.
So, slot types were always a tempting addition, but the problem was where to stop. If some slots are hidden sometimes, why not have many kinds of hidden slots hidden at various times? (This brings me right back to Us as a nice unification.) And you still, as you said, have the problem of reifying slots; in other words the problem solved with slot annotations.
- David
-- Jecel
David,
If the result could be a float, then there isn't much hope for an automatic extractor to know it couldn't be, unless I don't understand your example.
Sorry, I have two very different examples of how stuff could "leak". There really was no relation between the float problem and the string parsing so I shouldn't have mixed them like that.
At one point there may have been beginsWith:, and you could argue (and I would agree) that we would have been better off making error objects to pass in to fail blocks. The issue there is *not* exceptions vs fail blocks, but ratherfail-blocks with string arguments vs fail-blocks with object arguments.
Once again, the problem was with my description. I wanted to say what you just said but "exceptions" slipped in when trying to make an analogy of how the set of objects to be used as parameters might be organized. Many OO languages have a hierarchy of error and warning objects and I was thinking of something like that. I didn't mean to bring any other aspect of exceptions into the discussion (right now I am implementing a Squeak VM and am finding them very troublesome...)
Yes, of course. To me, "blocks" means blocks + block arguments + block locals + (what squeakers call closure semantics) + non-local return. Sorry if that wasn't clear.
You were clear, but I added the comment about non local returns just in case someone else following the discussion isn't aware of the importance of that aspect. In particular, this is something that is used very rarely in Smalltalk-80 compared to in Self.
["system slots" in tinySelf 0]
So, slot types were always a tempting addition, but the problem was where to stop.If some slots are hidden sometimes, why not have many kinds of hidden slots hidden at various times?(This brings me right back to Us as a nice unification.)And you still, as you said, have the problem of reifying slots; in other words the problem solved with slot annotations.
I tried several different designs going to both extremes (lots of implicit information vs explicit) and it isn't easy to find a good balance. Perhaps there isn't one, and each situation a different choice is the best.
Just as an example, in one version of tinySelf 0 (done before Self 3 came out, so this didn't have annotations) each slot had three words in the map: the name, a value and a code pointer. The system just called the indicated code with the supplied value (along with the receiver). The code for a constant slot (shared by all constant slots in the system) just returned the value, while the code for a data slot indexed the receiver. The code for a method that hasn't been compiled yet was the interpreter itself (the value is the bytecodes) while if it has been compiled it was the native code (the value is still the bytecodes, but that is ignored).
This Forth-like version has almost everything implicit. You can create many new types of slots, but there is no good wait of doing stuff with them other than their default behavior. The other extreme was to have each slot be a full object, which is hardly efficient in terms of memory but does make everything very explicit.
-- Jecel
self-interest@lists.selflanguage.org