NAME

Data::Binder - a map of keys to potential values for simple unification

SYNOPSIS

$binder = new Data::Binder(city => 'Denver', altitude => 5280);
if ($binder->bindable(city => 'Denver', population => 2000000))
    { ... }
if ($binder->bind(city => 'Dallas', altitude => 750))
    { ... }
if ($binder->bound())
    { ... }

ABSTRACT

A Binder is a special map of keys to potential values; it supports non-conflicting unification against other Binders or terms. Each key term in the Binder may be unbound (associated with an undef value), or bound to a defined scalar value. Unbound keys may be bound to anything, and bound keys may only be bound to identical values. Attempts to bind a new set of values succeeds completely or fails without changes.

Binders are useful in unifying a simple set of arguments to values, such as in languages like Prolog. Bind any lowercase arguments to themselves, and uppercase "variable" arguments to the caller's values. If that is not successful, then the rule is inappropriate.

They are also useful when a number of multi-faceted objects or strategies need to be tested against a single opportunity, but the available facets for each object or strategy are not always the same. Describe the facets with a hash, and the opportunity with a binder; inappropriate facet values will fail the unification.

METHODS

new()

my $binder = new Data::Binder(city => 'Denver', altitude => 5280);

Create a new binder, optionally with any number of key-value associations. Values may be undef, which indicate that the key is present but unbound.

put()

$binder->put(population => undef);

Forcefully assert new key-value associations into the binder. Replaces any existing value with the given value. Values may be undef, which indicate that the key is present but unbound.

bindable()

if ( $binder->bindable( city => 'Denver', $another_binder, ... ) )
    { ... }

Check whether all arguments are compatible with existing values in the binder. If given another binder reference, that binder's key-value pairs are tested in an arbitrary order. Everything else is assumed to be key-value pairs, and the pairs are tested in the order given; specifying the least likely bindable pairs first is a useful optimization.

Our term => undef can bind with any given term => undef.

Our term => undef can bind with any given term => value.

Our term => value can bind with any equal given term => value.

Our term => value cannot bind with a given term => different.

If any argument is unbindable, the returned value is undef. If all arguments are bindable to this binder's pairs, the returned value is not undef. In no case is any binder actually modified at any time.

bind()

if ( $binder->bind( city => 'Denver', $another_binder, ... ) )
    { ... }

Just as with $binder->bindable( ... ), try to bind all arguments to this binder's current key-value pairs.

If any argument is unbindable, the returned value is undef, and this binder is left unmodified. If all arguments are bindable to this binder's pairs, the returned value is not undef, and all given key-value pairs are asserted into this binder.

DESCRIPTION

A Binder internally keeps a hash for its terms, but limits the access to the main methods only. Just as with any Perl hash, any string can be a key, and keys are compared only by exact matches. All keys are unique in the Binder, but data values can be non-unique.

Keys may be associated with the undef data value, which for a Binder means that the key is declared but not bound to a given value. Such keys must be forced with put() or added in the initial new(), as the binding methods will not import new undef values.

If we adopt the hash notation of {K1=>D1, K2=>D2} to refer to Binders, we can describe the ways that Binders operate with a few examples. The K1 and K2 are the map's keys, and the D1 and D2 are the associated data values, respectively. We borrow the binding operator (=~) just to illustrate these examples. Both operands to this operator are Binders. The left-hand operand (the Binder object) may be modified, but the right-hand operand (passed as an argument) is never altered.

{CITY=>undef, ALTITUDE=>undef} =~ {CITY=>"Denver", ALTITUDE=>5280}
# Binds the CITY term to the new value "Denver", and ALTITUDE
# to the new value 5280; the result leaves the left binder
# identical to the right binder, with a true return value.

{CITY=>undef, ALTITUDE=>undef} =~ {CITY=>"Denver"}
# Binds just the CITY term to the new value "Denver"; the result
# replaces just the old value of CITY in the left binder, with a
# true return value.

{CITY=>"Denver", ALTITUDE=>undef} =~ {CITY=>"Denver"}
# Validates the existing binding of CITY to its value "Denver";
# result leaves everything unmodified, with a true return value.

{CITY=>"Denver", ALTITUDE=>5280} =~ {CITY=>"Denver", POP=>2000000)}
# Validates the existing CITY value and adds a new POP term;
# the result adds POP=>2000000 to the left binder,
# with a true return value.

{CITY=>"Dallas", ALTITUDE=>undef} =~ {CITY=>"Denver"}
# Cannot bind any terms because the CITY terms conflict; the result
# leaves everything unmodified, with an undef return value.

The whole binding operation either passes atomically, returning an arbitrary defined "true" value overall, or it fails, returning undef. If any term in a Binder fails to bind, then none of the elements are modified and an undef return is given. Only if all the terms could bind without conflict, then all the terms will be bound accordingly.

If data values are references, then they are compared only as identical references to a single entity. There is no provision for "deep comparison" of reference scalars.

AUTHOR

Ed Halley, <ed@halley.cc>

COPYRIGHT AND LICENSE

Copyright 1998-2003 by Ed Halley

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.