MT::Promise - Faux-lazy evaluation for Perl


    sub work_hard {
        # some costly operation

    use MT::Promise qw(delay lazy force);

    # slickest
    $meaning_of_life = lazy { work_hard(); return 42 };
    # kinda slick
    $meaning_of_life = delay(sub { work_hard(); return 42 });
    # clunky ...
    $meaning_of_life = new MT::promise(sub { work_hard(); return 42; });

    print force($meaning_of_life);
    # prints:
    # 42


A promise is like a value, but the value itself hasn't been computed yet. At any time, you can "force" the promise to get its value; the first time it's forced, the computation of the value takes place and the value is stored. Thereafter, forcing it uses the stored value instead of re-computing it.

This is useful if some bit of code may be expecting a value in a certain place, but you don't know up front whether that value will really be needed. To use this optimization, the expectant code needs to expect a promise, of course, since it has to call force to get the value. But you don't have to worry about whether the value will be needed or which bit of code will need it first--that will shake out at runtime.



There are three forms for creating promises. The lazy form is the slickest:

    my $red = foo($arg);
    my $value = lazy {
        $green = green($red);
        $blue = blue($red, $green);


The delay form can be useful if you want to delay the evaluation of a routine that already has a name:

   my $value = delay \&costly_function;

but it should be pointed out that costly_function in this case must be a thunk--it must not expect any arguments. Typically, you end up using a closure or the lazy form to encapsulate the arguments:

  my $value1 = lazy { fibonacci(8675309) };

This is almost precisely like $value2 = fibonacci(865309) except that (a) you have to force $value1, and (b) the 8675309th fibonacci won't be calculated if it's never forced.


Last and least, you can use new to create a promise, though it's clunkier than the other methods:

   my $value = new MT::Promise(sub { fibonacci(8675309) });

This would be useful if you were sub-classing promise for some reason.


Force the promise to get its value. The first time, the computation of the value happens and the value is stored for subsequent access.


Note that if you use the name of a variable declared outside the lazy block, such as

    my $red;
    my $value = lazy { foo($red) }

then $value will carry around a reference to $red for as long as $value lives. This is important to keep in mind as it's possible to create a circular reference without realizing it. You've got to try pretty hard, though.


Please see "AUTHOR & COPYRIGHT" in MT.