'''[ycl] shelf''', by [PYK], is a simple yet effective dynamic [object orientation%|%object system] similar in some ways to a [Programming with Prototypes%|%prototype]-based object system. ** Description ** Are you confused/bored/annoyed by class-based object systems? Then `shelf` might be the system for you! `shelf` provides the functionality needed to work with [namespace ensemble%|%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 it have been refactored into other components of [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 from a shelf, the commands of the original shelf are available through the interface of the new shelf. When a shelf is cloned, the commands and variables in the namespace of the original shelf are actually copied into the cloned shelf. A shelf has a `basis` command that can be used to directly access commands and variables in the shelf that forms its basis. `shelf` does not currently utilize [TclOO], but it certainly might in the future. ** Interface ** A shelf has the following subcommands: '''`$`''' ''`name`'': Returns the value of the first variable named ''name'' in the hierarchy of shelves that form the basis of the shelf. '''`$.locate`''' ''`name`'': Returns the [namespace qualifiers%|%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 a [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. '''`basis`''': The name of the shelf a shelf was cloned or spawned from, or, for a new shelf, the [empty string]. '''`clone`''': Like spawn, except that commands and variables in the namespace of the original shelf are actually copied into the new shelf, after the manner of `[ycl] ns dupensemble`. '''`disposal`''' ''`script`'': Arranges for ''script'' to be evaluated in the [global] namespace 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`'': Register a command as a method of the ensemble by adding it to the map and arranging for the fully-qualified name of the ensemble to be passed as the first argument to the command. `method` does not transform the command in any way, but it must already exist. '''`namespace`''': Returns the name of the namespace associated with the shelf. '''`spawn`''' '''''`name`''''': Create a new shelf that has the same interface as the original shelf. If ''name'' is the [empty string], automatically select an appropriate name. Returns the name of the new shelf. The namespace ensemble of the current shelf is copied to the new shelf, and occurrences in the map command prefixes of the fully-qualified name of the the current shelf are replaced with the name of the new shelf. '''`subcmd`''' ''`cmdname`'': Like `method`, but doesn't arrange for the name of the ensemble to be passed as the first argument to the command, it is not necessary for the command to first exist, and if the command name is not absolute, is resolved at invocation time relative to the namespace of the shelf. ** Utilities ** '''`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 [upvar%|%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 [http://chiselapp.com/user/pooryorick/repository/ycl/artifact/a7830a4032ea503f%|%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 ** ====== shelf 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 ====== ** Ensemble Methods ** A namespace ensemble, or nested namespace ensembles can be used as methods of a shelf. The trick is to use the ''-parameters'' switch of a [namespace ensemble]. Here's an example: ====== package require {ycl shelf} namespace import [yclprefix]::shelf::shelf shelf {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} subcmd generate generate [namespace which {heart of gold}] {heart of gold} generate infinite improbability ====== In the example above, the shelf is passed explicitly to the namespace ensemble command, `generate`, and the ''-parameters'' feature of namespace ensembles takes care of passing the shelf along until it reaches the target method, `improbability`. Note also that the target command, `generate`, is resolved relative to the namespace of the shelf at the time it is invoked. See the implementatino of `[ycl] matrix` for similar examples. <> object orientation | ycl