Okay you guys, you're running away from one kind of madness and proposing various other kinds of madness in its place. Mostly you're confusing what should be easy and what should be possible.
First a note about cleanup dependencies. In general, these should be driven by the structure of the data, not the structure of the program. If you want to make sure one piece of data is cleaned up before another, make sure there's a reference that enforces that. That's the principle on cleanup. It's also, more or less, the principle on initialization. What these various blocks are obscuring from your calculations is that there is a data structure for each of these sets of queued events, and as long as there's a way to name such a queue, it's not necessary to access it through the official "block" name. It would be nice if the property that stores a particular queue has a related name, of course. But for fancy things, we don't have to add options to the syntax of blocks in order to manipulate these queues. If you want to make sure you're last in the END queue, just manipulate that queue. We also don't need a new abstraction like "execution group", unless by that you simply mean one of these callback queues. We particularly don't need to orthogonalize all the names and make them equally obfuscational. This is very much a place where easy things should be easy, and hard things should be difficult. So here's the ruling. We don't need any special names for hacking on the queue in another module. So CHECK means UNITCHECK, always. There's no MAINCHECK name. If you want to do something fancy to the main program's CHECK list or END list, then at export time, modify the queue contained in Main's CHECKLIST property or ENDLIST property. If you really, really, really, want something to run last, then install something into Main::ENDLIST that looks to see whether there's anything after it when it gets triggered, and if so, reinstalls itself at the end again. And then we can have the entertaining situation of two or more modules both playing leapfrog trying to be last. Perhaps something fancy with priorities can be worked out, but if so, that's the domain of direct queue manipulation routines. It has nothing to do with the easy-to-use block declarations, which have to remain simple interfaces, or there's no point to them. So we're back to the standard four phase oriented blocks: my @x will begin {...} # at BEGIN time my @x will check {...} # at CHECK time (redefined to unit check) my @x will init {...} # at INIT time my @x will end {...} # at END time plus these four block oriented blocks: my @x will pre {...} # at PRE time (return boolean for DBC) my @x will enter {...} # at ENTER time (block entry) my @x will leave {...} # at LEAVE time (block exit) my @x will post {...} # at POST time (return boolean for DBC) plus three specialty times for things with lifetimes not related to blocks: state @x will first {...} # at FIRST time (first time through) has @.x will build {...} # at BUILD time (object init time) has @.x will destroy {...} # at DESTROY time (object final time) There's no LAST corresponding to FIRST because you can't generally tell when you've called a block for the very last time. (If there is a LAST, it'd be loop related, but then FIRST and LAST aren't really opposites, and it'd duplicate LEAVE anyway.) Oh, and I guess we still have the two "leave" variants: my @x will keep {...} # at LEAVE time, if leaving successfully my @x will undo {...} # at LEAVE time, if leaving unsuccessfully There's also, in a sense, a "catch" time, but you can't have more than one of those, so there's no CATCHLIST property to modify. If you're trying to add semantics to your CATCH, you're probably wanting an "undo" instead. A good case can be made for all of those--unless you happen to be against transactional programming, or design by contract. Or state variables, or objects, or blocks, or modules... :-) Larry