File: Promise.pm

package info (click to toggle)
movabletype-opensource 5.1.4%2Bdfsg-4%2Bdeb7u3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 32,996 kB
  • sloc: perl: 197,285; php: 62,405; sh: 166; xml: 117; makefile: 83; sql: 32
file content (143 lines) | stat: -rw-r--r-- 3,623 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# Movable Type (r) Open Source (C) 2001-2012 Six Apart, Ltd.
# This program is distributed under the terms of the
# GNU General Public License, version 2.
#
# $Id$

package MT::Promise;
use strict;
use Exporter;
*import                 = \&Exporter::import;
@MT::Promise::EXPORT_OK = qw(delay force lazy);

sub new {
    my ( $class, $code ) = @_;
    my $this = \$code;
    bless $this, $class;
}

sub delay {
    my ($this) = @_;
    __PACKAGE__->new($this);
}

sub lazy (&) {
    my ($this) = @_;
    __PACKAGE__->new($this);
}

sub force {
    my $this = shift;
    return $this if ( ref $this ne 'MT::Promise' );
    if ( ref $$this eq 'CODE' ) {
        $$this = $$this->(@_);
    }
    else {
        return $$this;
    }
}

1;

__END__

=head1 NAME

MT::Promise - Faux-lazy evaluation for Perl

=head1 SYNOPSIS

    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

=head1 DESCRIPTION

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 C<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.

=head1 USAGE

=head2 lazy

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

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

=head2 delay

The C<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 C<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 C<force>d.

=head2 new

Last and least, you can use C<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.

=head2 force

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

=head1 NOTES

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.

=head1 AUTHOR & COPYRIGHT

Please see L<MT/AUTHOR & COPYRIGHT>.

=cut