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.