[e-lang] Callback-less primitives (was Re: Debugging?)

Kevin Reid kpreid at mac.com
Fri Oct 5 00:10:45 EDT 2007


On Oct 1, 2007, at 20:54, Kevin Reid wrote:

> A vat is created whose implementation includes debugging support --  
> most likely, it is implemented using objects living in a normal  
> vat. The "host" vat can therefore run code which examines the  
> "debug-vat" objects and execution state to examine/step/modify them.
> ...
> This hasn't been done yet because it requires what I'll call a  
> "full VM" implementation of E: there must be no immediate callbacks  
> from primitive code to E objects, as that would make interruption  
> impossible.

MarkM and I discussed this just now. We could not think of any very- 
hard cases of immediate callbacks; here are some primitives which  
call back that we thought about:

   1. anything which coerces
   2. ConstMap#iterate/1
   3. __loop

1. Standard guard coercion calls __conformTo/1 on any object which is  
not of the required primitive type. This can be handled by the debug- 
vat executing, for example, 3#add/1 as if it were:

   def 3 { to add(other :int) { return __primitiveAdd(3, other) } }

That is, the invocation of int#coerce/2 is an explicit operation  
visible in the debugger, and is followed by a call of  
__primitiveAdd#run/2. __primitiveAdd does not coerce; it does a  
primitive type check (semantically equivalent to __approvedBy) which  
may fail but never invokes its argument's __conformTo.

2. ConstMap#iterate/1, for example, can be implemented as if it is:

   to iterate(f) {
     def keys := map.getKeys()
     var i := 0
     while (i < keys.size()) {
       f(i, map[keys[i]])
       i += 1
     }
   }

The primitive map operations for this purpose are then getKeys/0 and  
get/1, which have no reason to call back.

3. __loop is harder; its entire purpose is to call back. However,  
suppose it is implemented in E as:

   def __loop(f) {
     if (f()) {
       __loop(f)
     }
   }

This is a tail-recursive procedure, so it's fine as long as the  
evaluator does tail-call elimination. Now, doing so is undesirable in  
general for debugging, but in this particular case, every call frame  
is *identical*: __loop(<some-thunk>). Therefore, the debugger can,  
without discarding information, condense

   - __loop.run(<x>)
   - __loop.run(<x>)
   - __loop.run(<x>)

into

   - __loop.run(<x>) [x3]

Furthermore, this may be used to break upon certain numbers of loop  
iterations, or notice some kinds of apparently-infinite recursion.

-- 
Kevin Reid                            <http://homepage.mac.com/kpreid/>




More information about the e-lang mailing list