Data::Lazy.pm - "lazy" variables


NAME

Data::Lazy.pm - ``lazy'' (defered/on-demand) variables

version 0.6

(obsoletes and replaces Lazy.pm)


SYNOPSIS

  # short form
  use Data::Lazy variablename => 'code';
  use Data::Lazy variablename => \&fun;
  use Data::Lazy '@variablename' => \&fun;
  # to use options, you need to `use' the module first.
  use Data::Lazy;
  tie $variable, 'Data::Lazy', sub { ... }, LAZY_READONLY;
  # magic untie - slow on (broken) Perl 5.8.0
  tie $variable, 'Data::Lazy' => \$variable, sub { ... };


DESCRIPTION

A very little module for generic on-demand computation of values in a scalar, array or hash.

It provides scalars that are ``lazy'', that is their value is computed only when accessed, and at most once.

Scalars

  tie $variable_often_unnecessary, 'Data::Lazy',
    sub {a function taking a long time} [, $store_options];
  tie $var, 'Data::Lazy', 'a string containing some code' [, $store_options];
  use Data::Lazy variablename => 'code' [, $store_options];
  use Data::Lazy '$variablename' => \&function [, $store_options];

The first time you access the variable, the code gets executed and the result is saved for later as well as returned to you. Next accesses will use this value without executing anything.

You may specify what will happen if you try to reset the variable. You may either change the value or the code.

  1. LAZY_STOREVALUE
  2. In this mode - the default mode - changes to the variable are saved as if the variable was not tied at all. For example;
        tie $var, 'Data::Lazy', 'sleep 1; 1';
        # or tie $var, 'Data::Lazy', 'sleep 1; 1', LAZY_STOREVALUE;
        $var = 'sleep 2; 2';
        print "'$var'\n";

    will return:

        'sleep 2; 2'

  3. LAZY_STORECODE
  4. In this mode, writes to the variable are assumed to be updating the CODE that affects the value fetched, not the value of the variable.
        tie $var, 'Data::Lazy', 'sleep 1; 1', LAZY_STORECODE;
        $var = 'sub { "4" }'

    will return

        '4'

    with no delay.

    If you tie the variable with LAZY_STORECODE option and then undefine the variable (via undef($variable)), only the stored value is forgotten, and next time you access this variable, the code is re-evaluated.

  5. LAZY_READONLY
  6. In this mode, writes to the variable raise an error message via croak() (see the Carp manpage). That is,
        tie $var, 'Data::Lazy', 'sleep 1; 1', LAZY_READONLY;
        $var = 'sleep 2; 2';
        print "'$var'\n";

    Will give you an error message :

       Modification of a read-only value attempted at ...

  7. LAZY_UNTIE
  8. In this mode, the variable is untie'd once it has been read for the first time. This requires that a reference to the variable be passed into the `tie' operation;
       tie $var, 'Data::Lazy', \$var, "sleep 1; 1";

    Note that LAZY_UNTIE was not specified; the reference to the variable was automatically spotted in the input list.

It's possible to create several variables in one ``use Data::Lazy ...'' statement.

Array

The default tie mode for arrays makes individual items subject to similar behaviour as scalars.

eg.

  tie @variable, 'Data::Lazy', sub { my $index = shift; ... };
  tie @var, 'Data::Lazy', 'my $index = shift; ...';
  use Data::Lazy '@variablename' => \&function;

The first time you access some item of the list, the code gets executed with $_[0] being the index and the result is saved for later as well as returned to you. Next accesses will use this value without executing anything.

You may change the values in the array, but there is no way (currently) to change the code, other than (tied @foo)->{'code'} = sub {...} (which is considered cheating).

eg.

    tie @var, 'Data::Lazy', sub {$_[0]*1.5+15};
    print ">$var[1]<\n";
    $var[2]=1;
    print ">$var[2]<\n";
    tie @fib, 'Data::Lazy', sub {
        if ($_[0] < 0) {0}
        elsif ($_[0] == 0) {1}
        elsif ($_[0] == 1) {1}
        else {$fib[$_[0]-1]+$fib[$_[0]-2]}
    };
    print $fib[15];

Currently it's next to imposible to change the code to be evaluated in a Data::Lazy array. Any options you pass to tie() are ignored. Patches welcome.

The size of an array, as returned by evaluating it in scalar context or the $#var syntax, will return the highest index returned already - or 0 if nothing has been read from it yet. Note that this behaviour has changed from version 0.5, where 1 was returned on a fresh tied array.

Hash

 Eg.
  tie %variable, Data::Lazy, sub {a function taking a long time};
  tie %var, Data::Lazy, 'a string containing some code';
  use Data::Lazy '%variablename' => \&function;

The first time you access some item of the hash, the code gets executed with $_[0] being the key and the result is saved for later as well as returned to you. Next accesses will use this value without executing anything.

If you want to get or set the code that's being evaluated for the previously unknown items you will find it in $variable{$;}. If you change the code all previously computed values are discarded.

 Ex.
    tie %var, Data::Lazy, sub {reverse $_[0]};
    print ">$var{'Hello world'}<\n";
    $var{Jenda}='Jan Krynicky';
    print ">$var{'Jenda'}<\n";
    $fun = $var{$;};
    $var{$;} = sub {$_ = $_[0];tr/a-z/A-Z/g;$_};
    print ">$var[2]<\n";

If you write something like

  while (($key,$value) = each %lazy_hash) {
   print " $key = $value\n"; #
  };

only the previously fetched items are returned. Otherwise the listing could be infinite :-)

Internals

If you want to access the code or value stored in the variable directly you may use

    ${tied $var}{code}
    and
    ${tied $var}{value} # scalar $var
    ${tied @var}{value}[$i] # array @var
    ${tied %var}{value}{$name} # hash %var

This way you may modify the code even for arrays and hashes, but be very careful with this. Of course if you redefine the code, you'll want to undef the {value}!

There are two more internal variables:

    ${tied $var}{type}
     0 => scalar
     1 => array
     2 => hash
    ${tied $var}{store}
     0 => LAZY_STOREVALUE
     1 => LAZY_STORECODE
     2 => LAZY_READONLY

If you touch these, prepare for very strange results!

An object-oriented interface to setting these variables would be easily added (patches welcome).

Examples

 1.
 use Data::Lazy;
 tie $x, 'Data::Lazy', sub{sleep 3; 3};
 # or
 # use Data::Lazy '$x' => sub{sleep 3; 3};
 print "1. ";
 print "$x\n";
 print "2. ";
 print "$x\n";
 $x = 'sleep 10; 10';
 print "3. ";
 print "$x\n";
 print "4. ";
 print "$x\n";
 2. (from Win32::FileOp)
 tie $Win32::FileOp::SHAddToRecentDocs, 'Data::Lazy', sub {
    new Win32::API("shell32", "SHAddToRecentDocs", ['I','P'], 'I')
    or
    die "new Win32::API::SHAddToRecentDocs: $!\n"
 };
 ...

Comment

Please note that there are single guotes around the variable names in ``use Data::Lazy '...' => ...'' statements. The guotes are REQUIRED as soon as you use any variable type characters ($, @ or %)!


SIMILAR ALTERNATIVES TO THIS MODULE

There are several notable alternatives to this module; if you come across another, please forward mention to the author for inclusion in this list.

Memoize
Now a core module, this module performs similarly to the tied hash variant of this module. However, it is more geared towards static/global methods that already return the same value, whereas this module works on a per-object basis.

Object::Realize::Later
This module also provides for defered execution of code. This module ``expands'' objects to their full state via (declared) methods, and works via re-blessing objects into their new state. The principal advantage of this approach is that your reference addresses do not change, so existing pointers to these objects can stay as-is.

Tie::Discovery
Almost identical to the hash variant of this module, the principle extra feature provided by Tie::Discovery is that instead of a single code reference which must supply all fetched values, individual ``handlers'' are registered for each key for which values are wanted. This makes it particularly useful for configuration files.


BUGS

Due to incomplete support for tie'ing arrays in very old versions of Perl (ie, before 5.004), to fetch the size of an array, you cannot just evaluate it in scalar context; you have to use:

   tied(@a)->{'size'}

the usual;

   scalar(@a);  #  or ($#a + 1)

will return zero! :-(

AUTHOR

 Jan Krynicky <Jenda@Krynicky.cz>

COPYRIGHT

Copyright (c) 2001 Jan Krynicky <Jenda@Krynicky.cz>. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

Some changes copyright (c) 2004, Sam Vilain <samv@cpan.org>. All rights reserved. Changes distributed under terms of original license.

 Data::Lazy.pm - "lazy" variables