NAME

Attribute::Lexical - sane scoping of function/variable attributes

SYNOPSIS

    use Attribute::Lexical "CODE:Funky" => \&funky_attr_handler;
    sub thingy :Funky { ... }

    $handler = Attribute::Lexical->handler_for_caller([caller(0)],
                "CODE:Funky");

DESCRIPTION

This module manages attributes that can be attached to subroutine and variable declarations. Although it can be used directly, it is mainly intended to be infrastructure for modules that supply particular attribute semantics.

Meanings are assigned to attributes by code which is usually supplied by modules and which runs at compile time. The built-in mechanism for attribute control is awkward to use, difficult in particular to enable multiple attributes supplied by different modules, and it scopes attribute meanings according to the package of the object to which attributes are being applied. This module is intended to overcome these limitations.

This module supplies a simple pragma to declare an attribute, associating the attribute's name with a handler function that implements its semantics. The declaration is lexically scoped, lasting only until the end of the enclosing block. A declaration can be overridden, giving an attribute name a different meaning or making it meaningless, in an inner nested block.

Applying attributes

Attributes can be applied to variables or functions, where they are declared. A variable (which must be named) can have attributes added as part of a declaration with the my, our, or state keywords. Variables may be of scalar, array, or hash type. A function can have attributes added wherever the sub keyword is used: on a declaration of a named function, whether or not it defines the function body, or on an anonymous function.

An attribute list is introduced by a ":" character, and attributes are separated by ":" or whitespace. Each attribute starts with an identifier, and may also have a parenthesised string argument. See attributes for details.

Attributes for functions and our variables are applied at compile time. For anonymous functions that close over external lexical variables, the thing that has attributes applied is actually the prototype function, which stores the code but is not associated with any set of variables and so cannot be called. When a closure is created at runtime, it copies the state of this prototype, and does not get any attribute handling. Attributes for my and state variables, on the other hand, are applied at runtime, when execution reaches the variable declaration.

Attribute names

As noted in the previous section, each type of attribute that can be applied to an object is identified by a name, in standard identifier syntax. The same identifier can also have different meanings depending on the type of the object. So for the purposes of this module, an attribute is identified by the combination of object type and attribute identifier. These two parts are combined into one string, consisting of type keyword ("SCALAR", "ARRAY", "HASH", or "CODE"), ":", and identifier. For example, the name "CODE:Funky" refers to an attribute that can be applied to a function by syntax such as "sub foo :Funky".

Attribute identifiers that consist entirely of lowercase letters may have meanings built into Perl. Some are already defined, and others may be defined in future versions. User-defined attributes should therefore always have identifiers containing some other kind of character. Most commonly they start with an uppercase letter.

Handler functions

Each declared attribute is implemented by a handler function, which is a normal Perl function, and may be either named or anonymous. A single function may handle many attributes. When a declared attribute is to be applied to an object, the handler function is called with four arguments:

  • A reference to the target object. The handler function is expected to do something to this object.

  • The identifier part of the name under which the attribute was invoked. Normally not of interest, but possibly useful when reporting errors, in case the handler was attached to a different name from usual.

  • The attribute argument string. This is what appears between parentheses immediately following the attribute identifier. undef if there was no argument. A handler that is not expecting an argument should check that no argument was supplied.

  • A reference to the array returned by the caller function that describes the site where the attribute was invoked. This is mainly useful to implement lexical semantics, such as using the prevailing package in the interpretation of the argument.

Attribute handler functions are mainly called during compile time, but those for my and state variables are routinely called at runtime. Any handler can also be called as part of a string eval, when it is compile time for the code in the string but runtime for the surrounding code.

When a code attribute handler is called, the target function will not necessarily have its body defined yet. Firstly, a function can be pre-declared, so that it has a name and attributes but no body, and in that case it might never get a body. But also, in a normal function definition with a body, the attributes are processed before the body has been attached to the function (although after it has been compiled). If an attribute handler needs to operate on the function's body, it must take special measures to cause code to run later.

PACKAGE METHODS

All these methods are meant to be invoked on the Attribute::Lexical package.

Attribute::Lexical->handler_for_caller(CALLER, NAME)

Looks up the attribute named NAME (e.g., "CODE:Funky") according to the lexical declarations prevailing in a specified place. CALLER must be a reference to an array of the form returned by the caller function, describing the lexical site of interest. If the requested attribute is declared in scope then a reference to the handler function is returned, otherwise undef is returned.

This method is not available prior to Perl 5.9.4, because earlier Perls don't make lexical state available at runtime.

Attribute::Lexical->handler(NAME)

Looks up the attribute named NAME (e.g., "CODE:Funky") according to the lexical declarations prevailing at the site of the call to this method. If the requested attribute is declared in scope then a reference to the handler function is returned, otherwise undef is returned.

This method is not available prior to Perl 5.9.4, because earlier Perls don't make lexical state available at runtime.

Attribute::Lexical->handler_for_compilation(NAME)

Looks up the attribute named NAME (e.g., "CODE:Funky") according to the lexical declarations prevailing in the code currently being compiled. If the requested attribute is declared in scope then a reference to the handler function is returned, otherwise undef is returned.

Attribute::Lexical->import(NAME => HANDLER, ...)

Sets up lexical attribute declarations, in the lexical environment that is currently compiling. Each NAME must be an attribute name (e.g., "CODE:Funky"), and each HANDLER must be a reference to a function. The name is lexically associated with the handler function HANDLER. Within the resulting scope, use of the attribute name will result in the handler function being called to apply the attribute.

Attribute::Lexical->unimport(NAME [=> HANDLER], ...)

Sets up negative lexical attribute declarations, in the lexical environment that is currently compiling. Each NAME must be an attribute name (e.g., "CODE:Funky"). If the name is given on its own, it is lexically dissociated from any handler function. Within the resulting scope, the attribute name will not be recognised. If a HANDLER (which must be a function reference) is specified with a name, the name will be dissociated if and only if it is currently handled by that function.

BUGS

This module uses relatively new and experimental features of Perl, and is liable to expose problems in the interpreter. On older versions of Perl some of the necessary infrastructure is missing, so the module uses workarounds, with varying degrees of success. Specifically:

Prior to Perl 5.9.4, the lexical state of attribute declarations is not available at runtime. Most attributes are handled at compile time, when the lexical state is available, so the module largely works. But my/state variables have attributes applied at runtime, which won't work. Usually the attributes will be simply unavailable at runtime, as if they were never declared, but some rare situations involving declaring attributes inside a BEGIN block can confuse the module into applying the wrong attribute handler.

Prior to Perl 5.9.3, the lexical state of attribute declarations does not propagate into string eval.

Prior to Perl 5.8, attributes don't work at all on our variables. Only function attributes can be used effectively on such old versions.

This module tries quite hard to play nicely with other modules that manage attributes, in particular Attribute::Handlers. However, the underlying protocol for attribute management is tricky, and convoluted arrangements of attribute managers are liable to tread on each other's toes.

The management of handler functions is likely to run into trouble where threads are used. Code compiled before any threads are created should be OK, as should anything contained entirely within a single thread, but code shared between threads will probably have trouble due to Perl not properly sharing data structures.

SEE ALSO

Attribute::Handlers, attributes

AUTHOR

Andrew Main (Zefram) <zefram@fysh.org>

COPYRIGHT

Copyright (C) 2009, 2010, 2011, 2017 Andrew Main (Zefram) <zefram@fysh.org>

LICENSE

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.