74 lines
5.1 KiB
Markdown
74 lines
5.1 KiB
Markdown
In a picture, or when taking a picture, the frame is very important. It sets whatever is in the picture into context.
|
|
|
|
So it is a bit strange that having a **frame** had the same sort of effect for me in programming.
|
|
I made the frame explicit, as an object, with functions and data, and immediately the whole
|
|
message sending became a whole lot clearer.
|
|
|
|
You read about frames in calling conventions, or otherwise when talking about the machine stack.
|
|
It is the area a function uses for storing data, be it arguments, locals or temporary data.
|
|
Often a frame pointer will be used to establish a frames dynamic size and things like that.
|
|
But since it's all so implicit and handled by code very few programmers ever see it was
|
|
all a bit muddled for me.
|
|
|
|
My frame has: return and exceptional return address, self, arguments, locals, temps
|
|
|
|
and methods to: create a frame, get a value to or from a slot or args/locals/tmps , return or raise
|
|
|
|
### The divide, compile and runtime
|
|
|
|
I saw [Tom's video on free compilers](http://codon.com/compilers-for-free) and read the underlying
|
|
book on [Partial Evaluation](http://www.itu.dk/people/sestoft/pebook/jonesgomardsestoft-a4.pdf) a bit, and it helped to make the distinctions clearer. As did the Layers and Passes post.
|
|
And the explicit Frame.
|
|
|
|
The explicit frame established the vm explicitly too, or much better. All actions of the vm happen
|
|
in terms of the frame. Sending is creating a new one, loading it, finding the method and branching
|
|
there. Getting and setting variables is just indexing into the frame at the right index and so on.
|
|
Instance variables are a send to self, and on it goes.
|
|
|
|
The great distinction is at the end quite simple, it is compile-time or run-time. And the passes
|
|
idea helps in that i start with most simple implementation against my vm. Then i have a data structure and can keep expanding it to "implement" more detail. Or i can analyse it to save
|
|
redundancies, ie optimize. But the point is in both cases i can just think about data structures
|
|
and what to do with them.
|
|
|
|
And what i can do with my data (which is off course partially instruction sequences, but that's beside the point) really always depends on the great question: compile time vs run-time.
|
|
What is constant, can i do immediately. Otherwise leave for later. Simple.
|
|
|
|
An example, attribute accessor: a simple send. I build a frame, set the self. Now a fully dynamic
|
|
implementation would leave it at that. But i can check if i know the type, if it's not
|
|
reference (ie integer) we can raise immediately. Also the a reference tags the class for when
|
|
that is known at compile time. If so i can determine the layout at compile time and inline the
|
|
get's implementation. If not i could cache, but that's for later.
|
|
|
|
As a further example on this, when one function has two calls on the same object, the layout
|
|
must only be retrieved once. ie in the sequences getType, determine method, call, the first
|
|
step can be omitted for the second call as a layout is constant.
|
|
|
|
And as a final bonus of all this clarity, i immediately spotted the inconsistency in my own design: The frame i designed holds local variables, but the caller needs to create it. The caller can
|
|
not possibly know the number of local variables as that is decided by the invoked method,
|
|
which is only known at run-time. So we clearly need a two level thing here, one
|
|
that the caller creates, and one that the receiver creates.
|
|
|
|
### Messaging and slots
|
|
|
|
It is interesting to relate what emerges to concepts learned over the years:
|
|
|
|
There is this idea of message passing, as opposed to function calling. Everyone i know has learned
|
|
an imperative language as the first language and so message passing is a bit like vegetarian
|
|
food, all right for some. But off course there is a distinct difference in dynamic languages as
|
|
one does not know the actual method invoked beforehand. Also exceptions make the return trickier
|
|
and default values even the argument passing which then have to be augmented by the receiver.
|
|
|
|
One main difficulty i had in with the message passing idea has always been what the message is.
|
|
But now i have the frame, i know exactly what it is: it is the frame, nothing more nothing less.
|
|
(Postscript: Later introduced the Message object which gets created by the caller, and the Frame
|
|
is what is created by the callee)
|
|
|
|
Another interesting observation is the (hopefully) golden path this design goes between smalltalk
|
|
and self. In smalltalk (like ruby and...) all objects have a class. But some of the smalltalk researchers went on to do [Self](http://en.wikipedia.org/wiki/Self_(programming_language)), which
|
|
has no classes only objects. This was supposed to make things easier and faster. Slots were a bit like instance variables, but there were no classes to rule them.
|
|
|
|
Now in ruby, any object can have any variables anyway, but they incur a dynamic lookup. Types on
|
|
the other hand are like slots, and keeping each Type constant (while an object can change layouts)
|
|
makes it possible to have completely dynamic behaviour (smalltalk/ruby) **and** use a slot-like (self) system with constant lookup speed. Admittedly the constancy only affects cache hits, but
|
|
as most systems are not dynamic most of the time, that is almost always.
|