78 lines
5.2 KiB
Plaintext
78 lines
5.2 KiB
Plaintext
|
%p In a picture, or when taking a picture, the frame is very important. It sets whatever is in the picture into context.
|
|||
|
%p
|
|||
|
So it is a bit strange that having a
|
|||
|
%strong 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.
|
|||
|
%p
|
|||
|
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.
|
|||
|
%p My frame has: return and exceptional return address, self, arguments, locals, temps
|
|||
|
%p and methods to: create a frame, get a value to or from a slot or args/locals/tmps , return or raise
|
|||
|
%h3#the-divide-compile-and-runtime The divide, compile and runtime
|
|||
|
%p
|
|||
|
I saw
|
|||
|
%a{:href => "http://codon.com/compilers-for-free"} Tom’s video on free compilers
|
|||
|
and read the underlying
|
|||
|
book on
|
|||
|
%a{:href => "http://www.itu.dk/people/sestoft/pebook/jonesgomardsestoft-a4.pdf"} Partial Evaluation
|
|||
|
a bit, and it helped to make the distinctions clearer. As did the Layers and Passes post.
|
|||
|
And the explicit Frame.
|
|||
|
%p
|
|||
|
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.
|
|||
|
%p
|
|||
|
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.
|
|||
|
%p
|
|||
|
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.
|
|||
|
%p
|
|||
|
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.
|
|||
|
%p
|
|||
|
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.
|
|||
|
%p
|
|||
|
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.
|
|||
|
%h3#messaging-and-slots Messaging and slots
|
|||
|
%p It is interesting to relate what emerges to concepts learned over the years:
|
|||
|
%p
|
|||
|
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.
|
|||
|
%p
|
|||
|
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)
|
|||
|
%p
|
|||
|
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
|
|||
|
= succeed "," do
|
|||
|
%a{:href => "http://en.wikipedia.org/wiki/Self_(programming_language)"} Self
|
|||
|
%p
|
|||
|
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)
|
|||
|
%strong 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.
|