NAME
String::Defer - Strings with deferred interpolation
SYNOPSIS
my $defer = String::Defer->new(\my $targ);
my $str = "foo $defer bar";
$targ = "one"; say $str; # foo one bar
$targ = "two; say $str; # foo two bar
DESCRIPTION
String::Defer
objects provide delayed interpolation. They have concat (q/./
) overloading, which means that an interpolation like
"foo $defer bar"
will itself produce a String::Defer
object, and the stringification of $defer
will be delayed until that object is stringified.
METHODS
String::Defer->new(\$scalar | \&code)
This is the usual constructor (though concat
and join
can also be seen as constructors). The argument is either a scalar ref or a code ref, unblessed, and specifies a piece of the string that should be lazily evaluated. When force
is called, a scalar ref will be dereferenced and the referent stringified; a code ref will be called with no arguments.
It currently isn't possible to pass an object with ${}
or &{}
overloading; see "BUGS" below. If you wish to defer stringification of an object with stringify overloading, you need to pass a reference to (your existing reference to) the object, like this:
my $obj = Some::Class->new(...);
my $defer = String::Defer->new(\$obj);
It currently is possible to pass a ref to a scalar which happens to be holding a bare glob, like this:
my $targ = *FOO;
my $defer = String::Defer->new(\$targ);
but that may not be the case in the future. I'd like, at some point, to support passing a globref as a filehandle, and I'm not sure it's possible to distinguish between 'a ref to a scalar variable which happens to be currently holding a glob' and 'a ref to a real glob'.
It also is possible to pass a ref to a substring of another string, like this:
my $targ = "one two three";
my $defer = String::Defer->new(\substr $targ, 4, 3);
say $defer; # two
$targ = uc $targ; say $defer; # TWO
The String::Defer
will track that substring of the target string as it changes, but be aware that the target has to contain a long enough string at the time the substring is taken for this to work correctly.
$defer->concat($str, $reverse)
Concatentate $str
onto $defer
, and return a new String::Defer
containing the result. This is the method which implements the q/./
overloading.
Passing another String::Defer
will not force the object out to a plain string. Passing any other object with string overload, however, will. If you want to defer the stringification, wrap it in a String::Defer
.
$defer->force
Stringify the object, including all its constituent pieces, and return the result as a plain string. This implements the q/""/
overload.
Note that while this returns a plain string, it leaves the object itself unaffected. You can ->force
it again later, and potentially get a different result.
String::Defer->join($with, @strs)
Join strings without forcing, and return a deferred result.
Arguments are as for CORE::join
, but while the builtin will stringify immediately and return a plain string, this will allow any of $with
or @strs
to be deferred, and will carry the deferral through to the result.
Note that this is, in fact, a constructor: it must be called as a class method, and the result will be in that class. (But see "BUGS".)
djoin $with, @strs
This is a shortcut for String::Defer->join
as an exportable function. Obviously this won't be any use if you're subclassing.
BUGS
Please report any bugs to <bug-String-Defer@rt.cpan.org>.
Subclassing
Subclassing is currently rather fragile. The implementation assumes the object is implemented as an array of pieces, where those pieces are either plain strings, scalar refs, or code refs, but I would like to change this to something like a ->pieces
method. While it ought to be possible to override ->force
to create an object which builds the final string differently, it's not very clear how to best handle cases like an object of one subclass being concatenated with an object of another.
x
and x=
; other string ops
The repeat ops x
and x=
currently force deferred strings. It would be better if they produced deferred results, and better still if they could do so without duplicating the contents of the internal array. (Allowing the RHS to be deferred as well might be a nice touch.)
Much the same applies to all the other string ops. While functions like substr
and reverse
can't be overloaded, they can be provided as class methods. I suspect the best way forward here will be to provide a set of subclasses of String::Defer
, each of which knows how to implement one string operation. This would mean that ->join
would no longer return a String::Defer
, but rather a String::Defer::join
with internal references to its constituent pieces.
Objects pretending to be refs
Objects with ${}
and &{}
overloads ought to be accepted as stand-ins for scalar and code refs, but currently they aren't. In part this is because I'm not sure which to give precedece to if an object implements both.
Efficiency
The implementation of both concat
and join
is rather simple, and makes no attempt to merge adjacent constant strings. Join, in particular, will return a deferred string even if passed all plain strings, which should really be fixed.
AUTHOR
Ben Morrow <ben@morrow.me.uk>
COPYRIGHT
Copyright 2011 Ben Morrow <ben@morrow.me.uk>.
Released under the BSD licence.
SEE ALSO
Scalar::Defer for a more generic but more intrusive deferral mechanism.