Code objects

When this analysis was made in August 2008, the official Perl 6 Synopses mentioned several types of “code” objects that will be used by the P6 Standard Library. Some description and some members are given, but there were inconsistancies and it was incomplete.

This paper is an attempt to properly engineer this small set of classes.

General

Several names were used int he Synopses, apparently as type names. The wording of inevitably seemed old in not having the concept of Roles.

The fundimental purpose of each is given as follows:

The Callable role is the container interface for callable variables, using the & sigil. That is, anything that does Callable can be bound to such a variable. The function-calling method is present in this role.

The Block class represents any code object that provides a lexical scope for its execution. It is written that “all blocks are closures”, so a Block is any block of code or closure in the program.

The Routine is fancier than a Block. A Routine object represents subrutines of all kinds, including things defined using sub and method. Of particular interest is that a Routine can be wrapped in-place and is a mutable object, while Blocks are immutable.

What about the Code class

So what is the Code class? It does not have a strongly defined purpose in the synopses, and that is one of the points I wish to bring up. Perhaps it is redundant with the most abstract Callable role. It is used informally in the synopses to refer to any kind of code object, which would specifically be Block or Routine or user-defined container that does Callable. So I think those uses can simply be replaced by Callable.

The engineering of the class hierarchy may indicate more classes then explained here, for purposes of sharing code and elegance. But in the high-level specification, leaving a place-holder for “something that is a base class of Block but doesn’t provide scope” is not something we have a use for (all blocks are closures!). If other classes exist internally or become specified when this is fully developed, that’s fine. But let’s drop Code for now and correct the synopses to treat it as a synonym for Callable.

I beleive this was the intent when roles were introduced and the Callable role introduced, changing its name to an adjective.

Hierarchy

We have the following relationships between the types:

role Callable { ... }
class Block does Callable { ... }
class Routine does Callable { ... }

Note that although the Routine type is described above as a Block with more features, it is not defined as a derived type. For reasons that will be shown below, containment is a better choice. Perl 6 is not limited to simple bases classes as the only means of reuse and delegation.

The Callable role

The Synopses state that Callable contains the postcircumfix:<( )> method. This is the interface for the callable container (things using the & sigil).

When you call something, the compiler may check in advance for errors. If you manipulate a Callable object directly, you should be able to do these same checks. So, the method to get the signature should be present here too, not added in a more derived class. Remember, this is the complete interface for & variables, so it needs to provide everything needed to call code or implement a functor of some kind.

So, of the functions mentioned in the synopses, arity, callwith, and signature should be defined at this level.

In addition, we need a way to invoke the code while supplying modifiers to the call process itself, that are not passed through as parameters to the code being executed. Only that can make the Callable interface the complete interface used by the compiler to perform all the feats described of it. And, these features become available for explicit use, such as writing your own looping primitives that treat the supplied block in the same way as the built-in looping constructs. To this end, I propose the most general invoke method.

role Callable
  {
   method postcircumfix:<( )> (|$args) { ... }
   method arity() of Int { ... }
   method callwith (|$args) { ... }
   method signature() of Signature { ... }
   method assuming (|$args) of & { ... }
   our enum LoopType « First Next Last »;
   method invoke (
         Capture $args,   # sent to the formal arguments
         Bool :$inline = False,   # hide the call from context
	 LoopType :$loop,   # trigger FIRST/NEXT/LAST blocks
	 *%others      # concrete classes may have their own special things
   # ... others ...
  }

The Block class

The synopses only mentions a leave method, other than what is covered by Callable.

We need methods to access all the metadata, knowing this is indeed a concrete closure block. This includes any traits assigned to it, and convenience functions for accessing specific traits.

At the very least, this includes:

class Block does Callable
  {
   method Void leave (|$args) { ... } 
      # doesn’t actually return from that.
   method BEGIN is rw of Callable @ { ... }
   method CHECK is rw of Callable @ { ... }
   method INIT is rw of Callable @ { ... }
   method START is rw of Callable @ { ... }
   method FIRST is rw of Callable @ { ... }
   method PRE is rw of Callable @ { ... }
   method ENTER is rw of Callable @ { ... }
   method NEXT is rw of Callable @ { ... }
   method CATCH is rw of Callable @ { ... }
   method CONTROL is rw of Callable @ { ... }
   method LEAVE is rw of Callable @ { ... }
   method KEEP is rw of Callable @ { ... }
   method UNDO is rw of Callable @ { ... }
   method POST is rw of Callable @ { ... }
   method LAST is rw of Callable @ { ... }
   method END is rw of Callable @ { ... }
   method of is rw of Signature { ... }
   method as is rw of Signature { ... }
  }

A Block has an as property and an of property. Will defining the accessors of the same names work OK, as shown here?

Most of these are arrays of Callable, that are the various blocks defined as that thing within this block.

The Routine class

What is significant about Routine is that it may be updated in place. Other than that, what can subroutines do that other closure blocks can’t? After all, a sub doesn’t have to be named, and blocks can take arguments.

The Routine is essentially a container for a Callable (typically the Block, but you could put something strange there). The item assignment operator stores something else in that container. Perl 6 allows the relevent methods to be forwarded to that contained thing, so it does not have to be a base class to have “isa” semantics. It is not derived from Block, and is not limited to handling a Block.

class Routine  does Callable
  {
  has &.do is rw handles Callable;
  method name (
	Bool :$qualified,
	Bool :$strict
	) of Str  { ... }
  method names () of Str @ { ... }
  method wrap (&block) is RestoreHandle { ... }
  method multi () of Bool { ... }
  method name () of Str { ... }
  method infix:<=> (&newcode) { ... }
  # ... others ... 
  }

I changed wrap to return a RestoreHandle and made restore a method on that, as opposed to having an upwrap method in Routine.

Note the general assignment operator. This is implicit in the idea that this class is mutable, and it means that = is used instead of needing := to not have single-assignment semantics (but that’s another story).

The synopses describes a do property that is the actual code body. But, that is just the base class Block, which itself has the postcircumfix<()> operator and other methods, so how would the object returned by do be different from this object itself?

Note that a Routine has a name that may be queried by the contents or anything else. The name may be determined by how it is bound to the named value in the package or other scope when originally declared, but there is still some way to ask for its name without scanning symbol tables looking for it.