File Coverage

blib/lib/Data/Binder.pm
Criterion Covered Total %
statement 45 56 80.3
branch 15 24 62.5
condition 4 12 33.3
subroutine 9 10 90.0
pod 4 5 80.0
total 77 107 71.9


line stmt bran cond sub pod time code
1             # Data::Binder - a map of keys to potential values for simple unification
2              
3             #----------------------------------------------------------------------------
4             #
5             # Copyright (C) 1998-2003 Ed Halley
6             # http://www.halley.cc/ed/
7             #
8             #----------------------------------------------------------------------------
9              
10             package Data::Binder;
11 1     1   34998 use vars qw($VERSION);
  1         3  
  1         86  
12             $VERSION = 1.00;
13              
14             =head1 NAME
15              
16             Data::Binder - a map of keys to potential values for simple unification
17              
18             =head1 SYNOPSIS
19              
20             $binder = new Data::Binder(city => 'Denver', altitude => 5280);
21             if ($binder->bindable(city => 'Denver', population => 2000000))
22             { ... }
23             if ($binder->bind(city => 'Dallas', altitude => 750))
24             { ... }
25             if ($binder->bound())
26             { ... }
27              
28             =head1 ABSTRACT
29              
30             A Binder is a special map of keys to potential values; it supports
31             non-conflicting unification against other Binders or terms. Each key
32             term in the Binder may be unbound (associated with an undef value), or
33             bound to a defined scalar value. Unbound keys may be bound to anything,
34             and bound keys may only be bound to identical values. Attempts to bind a
35             new set of values succeeds completely or fails without changes.
36              
37             Binders are useful in unifying a simple set of arguments to values, such
38             as in languages like Prolog. Bind any lowercase arguments to themselves,
39             and uppercase "variable" arguments to the caller's values. If that is
40             not successful, then the rule is inappropriate.
41              
42             They are also useful when a number of multi-faceted objects or strategies
43             need to be tested against a single opportunity, but the available facets
44             for each object or strategy are not always the same. Describe the facets
45             with a hash, and the opportunity with a binder; inappropriate facet
46             values will fail the unification.
47              
48             =cut
49              
50             #----------------------------------------------------------------------------
51              
52 1     1   6 use warnings;
  1         2  
  1         34  
53 1     1   7 use strict;
  1         7  
  1         38  
54 1     1   6 use Carp;
  1         1  
  1         688  
55              
56             #----------------------------------------------------------------------------
57              
58             # $terms = $binder->_terms();
59             # Returns a clone of the current set of terms and values for this binder.
60             # Used internally to support atomicity in successful binding operations.
61             #
62             sub _terms
63             {
64 2     2   4 my $self = shift;
65 2         4 my $binding = { };
66 2         3 $binding->{$_} = $self->{terms}{$_} foreach (keys %{$self->{terms}});
  2         12  
67 2         7 return $binding;
68             }
69              
70             =head1 METHODS
71              
72             =head2 new()
73              
74             my $binder = new Data::Binder(city => 'Denver', altitude => 5280);
75              
76             Create a new binder, optionally with any number of key-value
77             associations. Values may be C, which indicate that the key is
78             present but unbound.
79              
80             =cut
81              
82             # $binder = new Data::Binder( 'key' => value, ... );
83             #
84             sub new
85             {
86 1     1 1 510 my $proto = shift;
87 1   33     7 my $class = ref($proto) || $proto;
88 1         4 my $self = { terms => { } };
89 1         3 bless $self, $class;
90 1 50       4 $self->put(@_) if @_;
91 1         3 return $self;
92             }
93              
94             =head2 put()
95              
96             $binder->put(population => undef);
97              
98             Forcefully assert new key-value associations into the binder. Replaces
99             any existing value with the given value. Values may be C, which
100             indicate that the key is present but unbound.
101              
102             =cut
103              
104             # $binder->put( 'key' => value, ... );
105             #
106             sub put
107             {
108 0     0 1 0 my $self = shift;
109 0 0       0 carp "arguments not even for term => value pairs" if (@_ % 2);
110 0         0 while (@_ >= 2)
111             {
112 0         0 my ($term, $value) = (shift, shift);
113 0         0 $self->{terms}{$term} = $value;
114             }
115 0         0 return $self;
116             }
117              
118             =head2 bindable()
119              
120             if ( $binder->bindable( city => 'Denver', $another_binder, ... ) )
121             { ... }
122              
123             Check whether all arguments are compatible with existing values in the
124             binder. If given another binder reference, that binder's key-value pairs
125             are tested in an arbitrary order. Everything else is assumed to be
126             key-value pairs, and the pairs are tested in the order given; specifying
127             the least likely bindable pairs first is a useful optimization.
128              
129             Our C<< term => undef >> can bind with any given C<< term => undef >>.
130              
131             Our C<< term => undef >> can bind with any given C<< term => value >>.
132              
133             Our C<< term => value >> can bind with any B given C<< term => value >>.
134              
135             Our C<< term => value >> cannot bind with a given C<< term => different >>.
136              
137             If any argument is unbindable, the returned value is C. If all
138             arguments are bindable to this binder's pairs, the returned value is not
139             C. In no case is any binder actually modified at any time.
140              
141             =cut
142              
143             # $bool = $binder->bindable( term => value, ... );
144             # $bool = $binder->bindable( $other_binder, ... );
145             #
146             sub bindable
147             {
148 2     2 1 3 my $self = shift;
149 2         7 my $binding = $self->_terms();
150 2         7 while (@_)
151             {
152 5 50 33     14 if (ref $_[0] and ref $_[0]->{terms})
153             {
154 0         0 my $other = shift;
155 0         0 unshift(@_, %{$other->{terms}});
  0         0  
156             }
157 5 50       13 carp "arguments not even for term => value pairs" if (@_ < 2);
158 5         8 my ($term, $value) = (shift, shift);
159 5 100 33     31 if (not exists $binding->{$term})
    100 33        
    50          
    50          
160 3         12 { $binding->{$term} = $value; }
161             elsif (not defined $binding->{$term})
162 1         4 { $binding->{$term} = $value; }
163             elsif (ref $value and $binding->{$term} != $value)
164 0         0 { return undef; }
165             elsif (not ref $value and $binding->{$term} ne $value)
166 0         0 { return undef; }
167             }
168 2         6 return $binding;
169             }
170              
171             =head2 bind()
172              
173             if ( $binder->bind( city => 'Denver', $another_binder, ... ) )
174             { ... }
175              
176             Just as with C<< $binder->bindable( ... ) >>, try to bind all arguments
177             to this binder's current key-value pairs.
178              
179             If any argument is unbindable, the returned value is C, and this
180             binder is left unmodified. If all arguments are bindable to this
181             binder's pairs, the returned value is not C, and I given
182             key-value pairs are asserted into this binder.
183              
184             =cut
185              
186             # $bool = $binder->bind( term => value, ... );
187             #
188             sub bind
189             {
190 1     1 1 2 my $self = shift;
191 1 50       5 carp "arguments not even for term => value pairs" if (@_ % 2);
192 1         5 my $binding = $self->bindable(@_);
193 1 50       3 return undef if not $binding;
194 1         3 $self->{terms} = $binding;
195 1         5 return $self;
196             }
197              
198             # $bool = $binder->bound( );
199             # $bool = $binder->bound( $term );
200             #
201             sub bound
202             {
203 6     6 0 249 my $self = shift;
204 6         10 my $terms = $self->{terms};
205 6 100       21 @_ = (keys %$terms) if not @_;
206 6         13 foreach (@_)
207 6 100       27 { return undef if not defined $terms->{$_}; }
208 3         16 return $self;
209             }
210              
211             #----------------------------------------------------------------------------
212              
213             1;
214             __END__