Re: simple object model (was: [self-interest] selfing Self)
- On Friday 30 March 2001 16:39, Steve Dekorte wrote:
> hash tables for "objects".
Rebol also has something like this.
In fact, I think that what we now have in Self is equivalent to this -
just add a set of hash table-like objects to mirrors. The problem I am
trying to attack is the existence of three levels:
1) base level: objects look like menus, morphs, strings, etc...
2) reflective level: objects look like hash tables (or collections of
3) implementation level: you have the actual memory format of objects
with tricks such as "maps" to help make cloned objects smaller
Since hash tables are a very abstract data type, saying that objects
are built from them still gives me no clue about the implementation
One complication in Self 4 is that levels 1 and 2 are in Self, but 3 is
in C++ (which requires different skills and tools). Self/R is meant to
simplify things by having level 3 in Self as well.
What I am investigating here is the possibility of bringing levels 2
and 3 closer together (ideally level 2 would be a slightly "sugar
coated" version of level 3).
- On Sunday 01 April 2001 18:07, Jecel wrote:
> In fact, I think that what we now have in Self is equivalent to thisEr... that should be "hash table-like methods to mirrors".
> - just add a set of hash table-like objects to mirrors.
- Jecel Assumpcao Jr wrote:
> 1) base level: objects look like menus, morphs, strings, etc...I'm not sure what you mean here.
> 2) reflective level: objects look like hash tables (or collections of
> 3) implementation level: you have the actual memory format of objects
> with tricks such as "maps" to help make cloned objects smaller
> What I am investigating here is the possibility of bringing levels 2
> and 3 closer together (ideally level 2 would be a slightly "sugar
> coated" version of level 3).
If you mean:
1) method invocation
3) object model
Then scripting languages like python and lua unite the first 2:
1) table.slotName() = call the returned function
2) a = table.slotName (get slot value, be it a function or data)
table.slotName = b (set slot value to data or function)
And Lua brings 3 (the implementation of the object model) up to the
level of 1&2 (the language itself) by providing "hooks" that let you
define what happens when a table lookup fails:
function index(table, slotName)
local parent = rawgettable(table, "_parent")
if parent then return parent[slotName] end
error("object "..tostring(table).." has no slot
You can likewise define the clone method, so you're free to do things
like NewtonScript style slot creation on write if you like, to conserve
memory and instantiating time at the expense of slot lookup time.
It doesn't get much more "light-weight" than this.
> > When I read about Self, I felt the same as in the case of Smalltalk.Yes. It is actually what you should do in this case. It looks not so
> > Later I discovered the absense of some needful possibilities. One of
> > them is constructing objects on the fly. One cannot write something
> > like this: (| x:Y: = (| :aX. :aY | ^(| x <- aX. y <- aY |) ) |).
> While this doesn't work because slot initializers ("aX" and "aY" in
> this case) are always evaluated in the context of the lobby object, you
> can get the same results with:
> (| x:Y: = (| :aX. :aY. new |
> new: (| x. y. parent* = traits clonable |) copy.
> new x: aX.
> new y: aY.
easy, so I don't like that approach.
>In my opinion, mirrors should not be used in ordinary applications. In
> The reason I used 'copy' above is that I am supposing that you don't
> want successive calls to this method to always return the exact same
> object, but just the same kind of object. If that is the case but you
> don't want the new object to be clonable, we could use the '_Clone'
> primitive instead of 'copy' and eliminate 'parent*'.
> If you wanted 'x' and 'y' to be constant slots in the new object (you
> didn't show that in your example) then things do get much more
> complicated. It is still possible to do this using mirrors, however.
addition, this solution doesn't look acceptable for frequent use.
It would be nice to have a construction equivalent to (| x = aX. y = aY
|) that is evaluated at run-time on every occurance.
>There is one problem with that. When you clone the object then the whole
> But I was indeed trying to make this kind of thing simpler.
> > So
> > now I'm in process of thinking out a more flexible but yet powerful
> > language. By now I have implemented two experimental versions of
> > Self. But I need something different.
> Me too, but more in the spirit of Self than the Self-inspired languages
> of the early 1990s.
> > > Like a simple object model: objects are just flat association lists
> > > (I'll use Lisp notation below) -
> > >
> > > (x 3 y 4)
> > flat list has an array structure rather than linked list?
> By "flat" I meant compared to this form of association list:
> ((x 3) (y 4))
structure should be duplicated, thus clones don't have any shared
attributes. In Self all objects have a special _map_ field that is
shared by all clones and that can be used in determining object 'type'.
Type information is used by the virtual machine for excluding expensive
In your case there no any type information about the object. So virtual
machine will have to perform lookup every time *any* message is sent to
So I would suggest to preserve the idea of using _map_ in each object
>This is the next problem.
> But it is a *very* interesting question if these should be arrays or
> linked lists -
> Lists: + can be shared as the continuation (CDR) of several other
> + can be scanned using a single pointer
> - since you can point into the "middle" of a list there are
> many aliasing problems
> - can't be indexed into
Imagine that every slot-access operation requires scanning through the
list of slots.
> + can be as space efficient as arrays with special encodingI vote for arrays. :-)
> + easy to split and put together
> Arrays: - need three items for scanning: base, index and size
> + no need to waste more than one virtual address per
> + access to any element is equally fast
>For example, you may consider introducing implicit parent for all
> It is easy to convert one into the other when you need to do some kind
> of processing for which the normal format is not very good.
> > Do you want to use the same semantics for a objects, arrays and
> > hashes? Like, (1 2 3) - array, (x 3 y 4) - object or hash?
> > Please describe that semantics a bit more.
> Every object is a list, but there are lists which are not formatted
> objects (the code examples below, for instance). So I must treat (1 2
> 3) as an object which does not explicitly list the messages it
> understands. In fact, the object "1" has this problem as well, but at
> least the number objects have special tags so they don't look like they
> might be a regular object. I am still figuring this out.
> > > append '(z 9) '(x 3 y 4)
> > Is this a message-sending construction? What is the receiver?
> No, this was a pseudo Lisp functional notation. This would be more
> '(z 9) append: '(x 3 y 4)
> though the problem above shows itself here: how does the '(z 9) object
> understand the 'append:' message? We would have to force it to be
> treated as a plain list, which is getting into the reflective level and
> similar to requesting a mirror for an object in Self.
objects. I.e. even empty object () would have a parent 'traits
Also I think that syntax (x 3. y 4) is more consistent.
Because code constructions use dot as a separator too.
>I thought about using the second variant too.
> This needs some careful design.
> > How to distinguish objects and methods?
> I am thinking of making methods internally be objects that have a
> special <code> "slot"
> ... factorial: (n 0
> <code> (n < 1 ifTrue:False: 1 '(n * factorial: n-1)
> ) ....
> I have other ideas for blocks than quoted lists, but this will do for
> now. Hmmm... if instead of "<code>" I used "|" then this would look
> more like Smalltalk...
>What notation should be produced by parser - Polish or direct?
> Note that though the argument "n" has a default value of 0, it will be
> replaced once this code object is cloned to create a context object.
> > Did you get rid off keyworded selectors like a:B:C:?
> I used one in the above example, but at the low level the keywords
> aren't spread among the arguments. In a higher level graphical editor I
> would expect you to be able to write something like
> n < 1 ifTrue: 1 False: [n * factorial: n - 1]
> My idea is that the arguments would be underlined, but that is hard to
> show using ASCII in this email.
How are you planning to handle 'resend.' and '_parent_.' constructions?
- On Thursday 05 April 2001 10:57, Uladzimir Liashkevich wrote:
> > [new object with slots filled from arguments]It is important to find out just how much this kind of thing is needed.
> Yes. It is actually what you should do in this case. It looks not so
> easy, so I don't like that approach.
A language should make common things easy, rare things possible. Of
course, there is a great danger of circular reasoning here: something
very useful could be rarely used because our current languages make it
so hard (or inefficient) to do.
> In my opinion, mirrors should not be used in ordinary applications.Very true.
> In addition, this solution doesn't look acceptable for frequent use.Where would this be used? Note that quoting conventions can become very
> It would be nice to have a construction equivalent to (| x = aX. y =
> aY |) that is evaluated at run-time on every occurance.
complex for the kind of expression you want. Some languages have
something similar for creating function creating functions.
I just noticed that since the assignment primitive returns the modified
object, the non constant slot, non cloning version of the example could
have been written more simply as
... ^ ( (| x. y |) x: sX) y: sY
> So I would suggest to preserve the idea of using _map_ in each objectWell, I did mention a not so transparent map-like variation:
((x : y :) 3 4)
The embedded list with the two indirect slots works as both a map and
as an anonymous parent (by the way, except for assignable parents and
directed resends do we actually gain anything by making parents visible
at the base level?). If you clone this, you will only take up three
words. And the shared list at the begining of this object and its clone
would be a sort of type information.
> For example, you may consider introducing implicit parent for allIn Self the primitive interface is much cleaner than in Smalltalk,
> objects. I.e. even empty object () would have a parent 'traits
since they look like normal message sends and use the normal send
bytecodes. But then things become a little strange - how do you look
them up? They have to be found in some hidden table deep inside the
virtual machine or else you couldn't send primitive messages to objects
> Also I think that syntax (x 3. y 4) is more consistent.I wasn't thinking of having this in the lowest level of code, but use
> Because code constructions use dot as a separator too.
an array of arrays instead:
((stuff 1) (stuff 2) (last stuff))
The user would not have to write it like this, of course (this is how
it works in Logo, for example).
> > My idea is that the arguments would be underlined, but that is hardDirect (infix). It really doesn't make a difference for a compiler, but
> > to show using ASCII in this email.
> What notation should be produced by parser - Polish or direct?
for an interpreter it would seem that you have to go with Polish or
RPN. Except I have a very nice interpreter I designed in 1983 which
sends the message *before* evaluating the arguments, so it can execute
infix code very well. The advantage is that by the time the value of an
argument has been calculated, the place where it will ultimately go has
already been created and so there is no need to save it on a local
stack only to later copy it to another stack.
I have recently found out that the last Lisp machine at LMI worked
exactly like this (it had an OPEN instruction and a separate CALL
instruction) so I can borrow their ideas in my hardware design. So not
only would my "machine code" (the arrays - thank you for your vote :-)
be very readable, but my "microcode" would as well.
> How are you planning to handle 'resend.' and '_parent_.'At the low level these things can be handled by primitives since they
are infrequent enough.
could be translated into
_PerformResend: 'copySize:' With: n
though this particular primitive is missing in Self 4.1.2 - you can
only do non-directed resends of unary messages. Actually, you can do
directed resends using the '_Perform:DelegatingTo:With:' type
primitives, as far as I can tell.
Thanks for your comments,