'''[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 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 is derived from the map of the current shelf, and adjusted so that the name of the new shelf is passed 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 new shelf so that the commands of the original shelf remain available. In contrast, when a shelf is cloned, the commands and variables in the namespace of the original shelf are actually copied into the cloned shelfa, and the namespace of the origanl shelf is not added to the path of the new shelf. `basis` is be used to directly access commands and variables in the shelf from which the current shelf was cloned or spawned. `shelf` does not currently utilize [TclOO], but it certainly might in the future. ** Interface ** A 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 [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 [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 the command 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 ====== ** A Different Approach to Initialization ** 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 modify 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 ** 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} 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 implementation of `[ycl] matrix` for similar examples. <> object orientation | ycl