[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: blocks



Jecel, thanks for your praise, and thoughts about blocks.

The eval message story may possible confuse meta-levels,
after all, how would you run eval (send eval?)?

This is interesting, Jecel, the story seems to rely on
local methods (methods in local slots) running
w/ lexical scoping. It may be in the right direction--not sure.

Another possibility, suggested to me by someone (I cannot remember
right now), is to change the rule that the slot initializer
runs in the context of the lobby, so that instead it
runs in the current lexical context.

In this story, the block is created as an object, not a method
in the local slot:

( |
  test = ( |
    i <- 0.
    b1 = ( | p* = traits block. value = ( i * i ). p2* = thisActivation | ).
    b1 value "returns i squared".
  | )
| ) test



not quite right, but close


At 4:47 PM 9/18/95, Jecel Mattos de Assumpcao Jr. wrote:
>Re-reading the ECOOP proceedings this weekend I was surprised
>to find out that I hadn't read "Programming as an Experience:
>The Inspiration for Self". I could hardly believe it as I was
>so sure I had read it twice already, but it seems I got it
>confused with some other paper ( it happens as we get older ;-).
>
>Anyway, it sure is great stuff! And all of the issues that
>I have raised here these last few days were mentioned in the
>paper and there were even some good answers. Rather than taking
>this as a sign that my idle speculations are not getting us
>anywhere, I couldn't resist taking a look at blocks.
>
>The story for blocks seems a little strange, as the following
>table shows:
>
>outer method = normal methods stored in constant slots
>block = the literal block object
>block context = the result of pushing a block on the stack
>value = the value method inside the block activation object
>inner method = literal anonymous methods used in Self 1.0
>
>              outer method   block    blockContext    value     inner method
>-----------------------------------------------------------------------------
>clones when
>fetched from
>const slot        YES           --           --          YES         --
>
>can be stored
>in data slot      NO            NO           YES         NO          NO
>
>clones when
>fetched from
>data slot         --            --           NO          --          --
>
>clones when
>pushed on
>stack             --            YES          --          --          YES
>
>new
>slot name       :self*          --        <scope>   <lexParent>* <lexParent>*
>
>new
>slot value     receiver         --      thisContext   same as     thisContext
>                                                     blockContext
>                                                      <scope>
>
>I found it very hard to justify the way that the value method of a block
>currently works. I could reduce blocks to mere syntactic sugar ( at the
>very high cost of using four temporary objects rather than the current
>two ) if Self had these two features:
>
>  1) local methods with the semantics of the old inner methods
>  2) the ability to manipulate running and dead Activations without
>     mirrors
>
>Item one means that the "silly" example I gave before would return
>2 instead of 4, but the "self marker" would not move as Randy
>proved it shouldn't. The idea is that a method activation is a
>refinement of an object, so a method defined within this refinement
>should result in a refinement of the refinement rather than in a
>refinement of the original object. This is a very forced view of
>things, but is not totally unreasonable. The "self marker" stays
>in place by having a <lexParent>* slot instead of a :self* one.
>
>I already have item two in tinySelf. This can be explained in terms
>of the implementation ( which is how I answered Mario when he asked
>why my "thisContext = (self)" example didn't run into infinite
>recursion ) or by looking into "slot types" as Mark explained about
>Glyphic Script. Here is a third alternative to explain this:
>
>I don't like that when a method object is cloned to produce an
>activation a ":self*" slot must be added. I want the method
>object to have this slot too, so it is a real prototype of the
>activation. Of course, its argument slots don't have any valid
>values yet and this would be nasty in the case of a parent slot
>like ":self*".
>
>What happens when we fetch an object from a slot? We can imagine
>that it is sent an "Eval" meta-message. For most objects, this
>would just return the object itself. For methods, it would clone
>it and fill in the arguments and schedule it for execution.
>What if this were achieved by placing a special value in the
>":self*" slot so that the method object would inherit this cloning
>behavior? In that case, once the arguments were filled in ( including
>the self slot ) then the activation could be stored in data slots
>and fetched without cloning - it would now inherit from a "normal"
>object and would handle "Eval" like normal objects - returning
>itself rather than a clone.
>
>So we have that in Self 4.0 activations are not true clones of
>methods, but both handle "Eval" in the same way. In tinySelf
>activations are true clones of methods, but handle "Eval" differently
>due to a different value in the dynamic parent slot. TinySelf is
>backwards compatible, however, as it is not possible to store
>an activation in a slot in Self 4.0 to see the difference.
>
>Now, supposing the above hasn't made you too sick to proceed :-)
>
>globals _AddSlots: ( | blockActivationProto = () | )
>
>blockActivationProto _Define: ( | parent* = traits block.
>                                  scope | )
>
>traits block _AddSlots: ( | value = (scope value) | )
>
>( | test = ( | i <- 0.
>               b1 = ( | value = ( (i * i) printLine ).
>                        bap = blockActivationProto |
>                        bap clone scope: _ThisActivation ).
>               b2 = ( | value = ( i factorial printLine ).
>                        bap = blockActivationProto |
>                        bap clone scope: _ThisActivation )
>             | .........
>               .........
>               .........
>               ... ifTrue: b1 False: b2.
>               .........
>            )
>| ) test
>
>This code causes the following four temporary objects to be
>create per block use:
>
>(1) a clone of b1 is created when it is fetched from its slot in the
>    "... ifTrue: b1 ..." part of the code. It has a <lexParent>* slot
>    ( rather than self* ) that is set to the current activation.
>
>(2) a clone of blockActivationProto is created when (1) is executed
>    and its scope slot is set to (1).
>
>(3) a clone of the "value" method is created when the ifTrue:False:
>    sends this message to (2). The method is found in traits block
>    and the self* slot is set to (2).
>
>(4) a clone of the "value" method is created when (3) executes. The
>    method is found in (1) and the <lexParent>* slot is set to
>    (1).
>
>Of course, not only are four objects a bit too much for a thing
>like this, but we also have a problem that (4) inherits from
>(1) which inherits from the original method activation ( which
>has the "i" slot, for example ). This means that (4) sees the
>"value" and "bap" slots in (1) ( but this could be "fixed" by
>using weird names like was done with "<lexParent>*" ).
>
>Please note that this could be done with mirrors in the b1 and
>traits block value methods if we didn't have the item 2)
>above, but this would mean yet another temporary object.
>
>While this is clearly not the solution to the "blocks problem",
>I think the answer is somewhere in that direction.
>
>Cheers,
>-- Jecel

-- Dave