Updated 2016-11-11 05:37:25 by pooryorick

ycl shelf, by PYK, is a simple yet effective dynamic object system similar in some ways to a prototype-based object system.

Description  edit

Are you confused/bored/annoyed by class-based object systems? Then ycl shelf might be the system for you!

shelf provides the functionality needed to work with namespace ensembles as objects in a dynamic object system. In this documentation, shelf signifies the namespace ensemble that is the handle for the object. The ensemble map defines the interface, and commands can be added to the map such that the name of the shelf is automatically passed to the command when called through the ensemble. The map is not limited to such commands, and indeed a new shelf contains in its map commands that don't take the name of the shelf as an argument. A shelf isn't offended in the slightest if the name of the ensemble is passed in some other position, or not at all.

When a shelf is deleted, the associated namespace is also deleted, and vice-versa.

The code footprint of shelf is quite small, and that's partially because components of a shelf have been refactored into other packages in ycl. ycl dupensemble provides the ability to produce new namespaces based on a shelf, and ycl var provides commands such as $, $.exists, and $.locate for reading variables up the hierarchy of shelves that form the basis of the current shelf.

When a new shelf is cloned or spawned, the commands of the map of the new shelf are derived from the map of the current shelf and adjusted so that the name of the new shelf is occurs in place of the name of the original shelf. This adjustment only happens for items in the lists at the top level at the map, so some care should be taken to form the map such that cloning and spawning has the desired effect

when a shelf is spawned, the namespace of the original shelf is added to the namespace path of the new shelf so that the commands of the original shelf remain available. The shelf a shelf was spawned from is known as the basis, and variables and commands in the basis remain available to the spawned shelf. This mechanism can be used to form a hierarchy of shelves, implementing the prototype pattern.

When a shelf is cloned, the commands and variables in the namespace of the original shelf are actually copied into the cloned shelf, and the namespace of the original shelf is not added to the path of the new shelf. A clone is designed to be as indistinguishable as possible from the shelf it was cloned from. Once it is cloned, it can be transformed to taste.

shelf does not currently utilize TclOO, but it certainly might in the future.

Interface  edit

The primordial shelf has the following subcommands:
$ name ?value?
Returns the value of the first variable named name in the hierarchy of shelves that form the basis of the shelf. If value is provided, it is first assigned to that variable. The assignment always occurs in namespace of the current shelf. To set variables by the same name in shelves that are the basis of the current shelf, use $.locate or basis to work back to the desired shelf and variable.
$.locate name
Returns the fully-qualified name of the first variable named name in the hierarchy of shelves that form the basis of the shelf, or an error if a matching variable is not found.
$.exists name
Like $.locate, but returns True only if a matching variable was found.
~
Executed just prior to the deletion of the ensemble.
apply routine args
like apply, but the first argument to the routine is the name of the shelf, and the routine is evaluated in the namespace of the shelf.
attribute name
Creates a method named name that returns the current value of the corresponding variable, and if called with one argument, first assigns that argument to the variable.
basis
Returns the name of the shelf spawned from, or, for a new shelf, the empty string. basis is used to directly access commands and variables in the shelf from which the current shelf was spawned.
clone
Clone the current shelf. Commands, variables, and configuration of the original shelf are copied to the clone, and occurrences of the fully-qualified name of the original shelf in the ensemble map are replaced with the full name of the clone, as documented for ycl ns dupensemble. The basis of a clone is the same as the basis of the shelf it was cloned from.
commands
A list of the commands sitting on the shelf.
disposal method
Arranges for method to be invoked when the ensemble is deleted. This command is currently additive. with no way to delete any already-registered scripts, except by using trace directly. It isn't anticipated that a more flexible mechanism would be needed, but it could be added at some point.
eval script
Evaluate script in the namespace of the ensemble.
method cmdname ?command prefix? ?args?
Appends the fully-qualified name of the ensemble to command prefix and adds it to the ensemble map, followed by the items in args. If a command prefix is not given, the namespace tail of cmdname is used as a single-item command prefix. If the first word of command prefix is not a fully-qualified command name, it is resolved at the time of invocation relative to the namespace of the current shelf. When the first word of command prefix is fully-qualified, a more simple and performant mapping is used, but then in order to override the command, any cloned or spawned shelves should call method again.
namespace
Returns the name of the namespace associated with the shelf.
shadowed method ?arg? ...
Invoke the method named method, making sure that it isn't resolved in the current namespace. This is the way to invoke a method that has been overriden by the current method or in the same namespace as the current method. shadowed is described in The Anatomy of an Object System.
spawn name
Create a new shelf and copy the configuration of the original shelf to the new shelf. The namespace of the original shelf and items in its namespace path are prepended to the namespace path of the new shelf so that commands in the original shelf are still available. The ensemble map is adjusted in the same way as it is for clone, i.e. as documented for ycl ns dupensemble. If name is the empty string, an unused name is automatically chosen. The absolute name of the new shelf is returned.
subcmd cmdname
Like method, but doesn't arrange for the name of the ensemble to be appended to command prefix.
switch ?shelf shelf? cmdname ?arg? ...
Evaluate the command named cmdname as if shelf was the current shelf. By default, shelf is the basis of the current shelf. This is the shelf equivalent of next for TclOO. To evaluate a command through switch at the same level as the caller of switch: uplevel [list $_ switch mycmd ...] .

Utilities  edit

package require {ycl shelf util}
asmethod spec
spec is a method specification of the form
args objvars nsvars body
Where args and body are the standard arguments to proc. objvars, is a list of names of variables that belong to the shelf, and nsvars is a list of names of variables in the namespace of the command. asmethod transforms body such that variables objvars and nsvars are linked in the local scope of the procedure to the appropriate variables. This allows the procedure body to be written in an a manner that is agnostic to the particular object system in use, since it need only concern itself with local variables. Other object systems may use the same spec, linking the local variables as appropriate for that system.
shelf uses this internally, so an example, although a bit convoluted at the moment (the refactoring is in progress), can be found here
glass
Configure a shelf such that any command in its associated namespace is automatically exposed as a subcommand of the shelf.
shelf
Create a new shelf.

Example  edit

shelf spawn swallow 
swallow $ airspeed 9 
proc fly _ {
    upvar 0 [$_ $.locate airspeed] airspeed
    puts "$_ is flying at a speed of $airspeed!"
}
swallow method fly 
swallow fly

swallow spawn european
european $ airspeed 11
european $ airspeed ;# -> 11
european fly

A Different Approach to Initialization  edit

A shelf is commonly given a command named init that serves to set the shelf to some initial state. In contrast with many other object systems, this initialization doesn't occur automatically when a shelf is cloned or spawned. This provides the opportunity to tailor a cloned or spawned shelf before initializing it. Like clone and spawn, init commonly returns the name of the shelf, allowing a pattern like this:
[some_shelf spawn newshelf] init key1 val1 key2 val2 ...
newshelf dostuff...

To assign the full name of the shelf to a variable, particularly when arranging for a name to be automatically chosen, this becomes:
set newshelf [some_shelf spawn {}] init key1 val1 key2 val2 ...
$newshelf dostuff...

Ensemble Methods  edit

A namespace ensemble, or nested namespace ensembles can be used as methods of a shelf. The trick is to use the -parameters switch. Here's an example:
package require {ycl shelf shelf}
namespace import [yclprefix]::shelf::shelf

shelf spawn {heart of gold}

namespace eval generate {
    namespace ensemble create -parameters _
    namespace export *

    namespace eval infinite {
        namespace ensemble create -parameters _
        namespace export *

        proc improbability _ {
            puts [list $_ probability approaching infinity!]
        }
    }
}
{heart of gold} method generate


{heart of gold} generate infinite improbability

In the example above, method arranges for the to be passed to generate, and the -parameters feature of namespace ensembles takes care of passing that argument along along until it reaches the target method, improbability. Note also that the generate, is resolved relative to the namespace of the shelf at the time it is invoked.

See the implementation of ycl matrix for similar examples.