This design document proposes that TT3 will bless the hash array of named parameters that it passes to subroutines into a specific class (Template::Type::Params). This will allow the called subroutine to recognise the difference between named parameters provided by TT and any other reference to a hash array.
TT2 does some magical handling of arguments passed to a subroutine or method call. All named parameters are collected in a hash array and passed as the last argument.
[% foo(10, 20, x=50, 30, 40, y=60) %] [% foo(10, 20, x=>50, 30, 40, y=>60) %] # same as above in TT2
The foo()
subroutine receives the arguments as if they were
written:
[% foo(10, 20, 30, 40, { x => 50, y => 60 }) %]
This is discussed further in TT3 Language Design Document 3 - Arguments.
The problem is that the subroutine or method called has no way of differentiating between named parameters and a reference to any other hash array.
[% myhash = { x=10, y=20 };
mycode(myhash); # these three calls are all
mycode(x=10, y=20); # indistinguishable as far
mycode({ x=10, y=20 }); # as mycode is concerned
%]
This may not be a problem if your subroutine doesn't expect any hash
references other than the named parameters. For example, you might write
mycode
to look for an optional hash reference passed as the
last argument like this:
sub mycode { my $params = @_ && ref $_[-1] eq 'HASH' ? pop(@_) : { }; my @args = @_; # do something }
However, if your subroutine is expecting a list of hash references and named parameters are optional, then the following will not work as expected.
[% mycode(hash1, hash2, hash3) %] # assumes hash3 is named params
Whereas this will work just fine:
[% mycode(debug=1, hash1, hash2, hash3) %]
In this case, the presence of the debug
named parameters
causes TT to add the extra hash reference of named parameters to the
arguments.
To solve this problem, TT3 will first bless any named parameters into the
Template::Type::Params
class.
# TT3 use constant PARAMS => 'Template::Type::Params'; sub mycode { my $params = @_ && ref $_[-1] eq PARAMS ? pop(@_) : { }; my @args = @_; # do something }
The Template::Type::Params
class is a direct subclass of
Template::Type::Hash
which implements the hash virtual
methods. That means that you can call any of the hash virtual methods on
the object.
print $params->join(' => ', "\n"); my $keys = $params->keys; # etc
However, the object is also intended to be used transparently as a simple hash reference. Yes, it is an object, but only for the purposes of identification and to provide methods of convenience for the programmer. We're not worried about the encapsulation side of OO programming in this case so don't feel scared about digging right into the parameters. It really is just a "smart hash".
print $params->{ key1 }; # accessing hash items is officially OK
The Template::Type::Params
module defines the
PARAMS
constant to save you from doing it (although you
still have to load the module of course).
use Template::Type::Params 'PARAMS'; sub mycode { my $params = @_ && ref $_[-1] eq PARAMS ? pop(@_) : { }; ... }
The Template::Utils
module defines the
tt_args_opts()
subroutine as a helper function to extract
named parameters from the argument list.
use Template::Utils 'tt_args_opts'; sub mycode { my ($args, $opts) = tt_args_opt(@_); print "positional arguments are: ", join(', ', @$args), "\n"; print "named parameters are: ", $opts->join, "\n"; }
Note that the $opts
named parameters are a blessed hash
reference, but the positional arguments in $args
are a
regular unblessed list reference.
(NOTE: this helper subroutine should probably be called
tt_args_params()
if we're going to be talking about
"params". However there are some other places where I've used "opts" in
the code base as the generic term for the optional named parameters.
Might be confusing. Probably warrants revisiting and either clarifying
why there are two names or picking one and sticking with it).
Any code currently testing for a HASH
reference as the last
argument to detect named parameters will need to be changed. The changes
required would be minimal.
TT3 Language Design Document 3 - Arguments