Re: there is a subtle problem doing: mySlot := obj getSlot(slotname)

Jeremy Tregunna <[email protected]>
Newsgroups gmane.comp.lang.io
Message-ID <[email protected]>
On 2011-11-08, at 7:41 AM, Mildred Ki'Lya wrote:

> 
> 
> The problem with what the OP is trying to do is that we store the activation bit on objects themselves. This means, that when you get a reference to the object, it retains its activation bit (it has to, otherwise we break code). We could have implemented this differently in the core, but we didn't. As such, when you use getSlot() you get a reference to the object itself, activation bit set and all. The way the evaluator (perform()) works is basically this:
> 
> 1. Check if there's a cached result on the message
> 1a. Return it if so
> 2. Look up the message name on the receiver
> 2a. If we find it, go to 4.
> 2b. If we don't find it, call forward.
> 2c. If we exhaust our parents and still can't find the slot, throw an error
> 3. Call the method, return the result.
> 4. Check its activatable bit.
> 4a. If set, go to 2, but look up "activate"
> 5. If all else fails, return our value (unactivated).
> 
> There's really nothing more to the evaluator. There are minor variations you can make on the evaluator, but by and large that's the general algorithm.
> 
> For Acute, I had, for a while, introduced an intermediate object called a "Slot" which was stored in the slot table. This Slot object had a reference to the actual value held in, as well as the activation bit. If in the OP case, when the slot := ... assignment occurs, a new Slot object gets created, with the referenced value held it its data slot, without the activation bit set. Which means when you try and call it by name, instead of activating you'd get the actual Block object. This is not an algorithm chosen by the mainline Io implementation, and actually one I've abandoned for Acute.
> 
> This is a common gotcha, and a source of many pains if you are writing library code. You can never be sure if a user is passing in an activatable object, so you have to account for that.
> 
> Is it so much a problem in library code, can't we assume that we don't accept method objects? 

Well, you can assume that, but then you litter your code with checks which still require a getSlot to verify the argument isn't a Block type. I.e., you think just restricting the objects users can pass around and making your implementation more complicated in doing so, requiring more time even to execute.

> 
> If this isn't mentioned somewhere, it should be. That said, perhaps it's time we figured out a long term solution to this problem which doesn't affect performance quite as much as my Slot object idea did (impacted the runtime by approximately 35% of additional overhead).
> 
> That's not an easy solution ... For a long time (before I knew about Self and Io), i tried to design a language that had methods as first-class objects. I never really succeeded.

Believe me, I know. Before I had the Slot idea, it was Jan-Paul Bultmann being super annoyed with this problem that it finally became more than a minor annoyance for me (and I've written a plethora of Io code since 2003, perhaps I just got used to it). Which is why the Slot object/meta idea came about. We talked for about 2 days figuring out solutions, and this is the one that we agreed was workable.

> Perhaps we should look at how Self or NewtonScript can do that. I admit I never looked long enough to grasp this kind of details.
> 
> Now, if I think about it, what is that exactly that we want? We want to:
> - (1) be able to mix methods and data in slots. Methods slots being called, and data slots being taken as they are
> - (2) pass blocks as arguments and have them called when the value is required
> - am i missing something there?

In Io, behaviour is a special kind of state, and that's a core requirement. As such, methods/blocks are valid values. If you use the method() convenience constructor, the activatable bit is set by default. If you use the block() constructor, it's not set by default. That is to say:

foo := method(1)
bar := block(1)

list(foo, bar)

Will return a list whose first value is 1, but whose second value is a Block object. You must explicitly pass call() to the Block object in order to invoke it. Generally, if you are accepting a function, you should pass around blocks. That said, nothing negates users from passing methods.

> To solve (1), the best solution I can think of is to store alongside with the slot name in the object, a boolean telling if the object has to be activated. But how do we set this boolean value? We can either use a different operator (Lisaac, and perhaps Self, uses := for data slots and <- for code slots)
> 
> As for (2), the purpose is for the block to be transparent, and behave just as the value it would return. So, we want to keep the perform step just as it is and keep the activation bit in the object.
> 
> To sum this up, blocks objects keep their activation bits, but we add another activation bit with the slot name in objects. When we lookup a slot, if either the slot or the object have the activation bit set, the activation step is performed. Methods would have the activation bit in the slot and Blocks would have the activation bit in the block object.
> 
> By the way, a few weks ago I expressed my interest in the acute project, and my desire to contribute. But I don't. I'm sorry about that but that's because I can't understand the roadmap and the desired architecture of acute. I'm still following the commits with much interest.

No worries, it's still fairly young, and it's part of a very long term project that involves building not only the VM I'm spending all my time on now basically, but a bytecode compiler, JIT interpreter and after that, a debugger. As such, the last 5 weeks I haven't touched Acute at all. I fully expect there to be more interest once there's more to show than a semi-functional VM, and a mostly complete prototype bootstrap interpreter written in ruby. :)

Regards,

Jeremy Tregunna
I'm available via e-mail at [email protected], or facetime at [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.