Group
Extension

Data-Bool/lib/Data/Bool.pod


=encoding utf8

=head1 NAME

Data::Bool - An interface to booleans as objects for Perl

=head1 VERSION

version 2.98014

=head1 SYNOPSIS

    use Data::Bool qw(true false is_bool to_bool);

    $true  = true;
    $false = false;

    is_bool(true);     # true
    is_bool('xxx');    # false

    to_bool(1);        # Data::Bool::true()
    to_bool('');       # Data::Bool::false()

=head1 DESCRIPTION

Perl has no native representation for booleans.
Most of the time the Perl concept of truth is enough.
But when dealing with serialization of formats
which support booleans, it is desirable to keep the
booleans intact on round trips, eg. when writing after loading.
And there are other good reasons for that,
like strict validation via various mechanisms, like schemas,
OpenAPI, type hierarchies, etc.

A solution for that was adopted for JSON modules
around 2012 by using references to C<1> or C<0>
blessed into L<JSON::PP::Boolean> which was chosen
as the canonical package for these objects.

The problem with that was the coupling with L<JSON::PP>
for no apparent good reason. Booleans are independent
of JSON and this association makes little sense
when loading documents in formats like YAML, MessagePack, BSON, etc.
However, the integration of the concept of boolean
for all these applications is quite convenient.

Marc Lehmann's L<Types::Serialiser> approached this problem
by creating a common interface used by L<JSON::XS> and L<CBOR::XS>
modules. This module lifts this core concept (including
idea, implementation and documentation) into an isolated treatment
for booleans only – so this may work as a common
ground for interoperability on booleans as objects for Perl modules.

The implementation keeps the compatibility with the
previous agreement on C<JSON::PP::Boolean> by making
the C<Data::Bool> implementation stash an alias for
C<JSON::PP::Boolean>.

That means

    Data::Bool::true->isa('JSON::PP::Boolean');

but also

    Data::Bool::true->isa(Data::Bool::BOOL_PACKAGE);

This also allows the optimization of an isa test
to a direct comparison of stash pointers. That is,

    ref Data::Bool::true eq Data::Bool::BOOL_PACKAGE

is equivalent to

    ref Data::Bool::true eq 'JSON::PP::Boolean'

=head2 INTERFACE

L<Data::Bool> has two ready-to-use instances for true and false.

    Data::Bool::true()

    Data::Bool::false()

L<Data::Bool> true values are represented as a reference
to a scalar containing C<1> – implementations are allowed
to directly test for this. For example, one can tell
if a value is a L<Data::Bool> true by using this:

    Data::Bool::is_bool($value) && $$value

L<Data::Bool> false values are represented as a reference
to a scalar containing C<0> – implementations are allowed
to directly test for this.

One can test if a value is a L<Data::Bool> boolean with

    Data::Bool::is_bool($value);

Converting from a Perl true or false value into L<Data::Bool>
booleans can be done with

    Data::Bool::to_bool($value);

Independent boolean objects may be produced from a Perl true
or false value by using

    Data::Bool::BOOL_PACKAGE->new($value);

Also part of this interface is a few overloaded
operators for L<Data::Bool> booleans.

    # bool
    true  ? 'yes' : 'no';    # 'yes'
    false ? 'yes' : 'no';    # 'no'

    # 0+
    0+ true;     # 1
    0+ false;    # 0

    # ""
    true . '';     # 1
    false . '';    # 0

=head2 BASIC CODE

The code of this module would look as below, if not
for the efforts to play nice with L<JSON::PP>, L<Types::Serialiser>
and L<Cpanel::JSON::XS> modules and old versions of perl.

    use 5.006;

    package Data::Bool::Impl;

    use overload (
        '0+' => sub { ${ $_[0] } },
        '++' => sub { $_[0] = ${ $_[0] } + 1 },
        '--' => sub { $_[0] = ${ $_[0] } - 1 },
        fallback => 1,
    );

    sub new { bless \( my $dummy = $_[1] ? 1 : 0 ), $_[0] }

    package Data::Bool;

    use Exporter 1.57 'import';
    use Scalar::Util ();

    our @EXPORT_OK = qw(true false is_bool to_bool BOOL_PACKAGE);

    use constant true  => Data::Bool::Impl->new(1);
    use constant false => Data::Bool::Impl->new(0);

    use constant BOOL_PACKAGE => ref true;

    sub is_bool ($) { Scalar::Util::blessed( $_[0] ) and $_[0]->isa(BOOL_PACKAGE) }

    sub to_bool ($) { $_[0] ? true : false }

=head1 FUNCTIONS

L<Data::Bool> implements the following functions, which
can be imported individually.

=head2 BOOL_PACKAGE

    $package = Data::Bool::BOOL_PACKAGE;

The implementation package of the boolean objects.

It is recommended to always use this instead of
C<'JSON::PP::Boolean'> or C<'Data::Bool::Impl'> for maximum
compatibility. It works correctly with C<ref $bool> comparisons
or isa checks.

=head2 false

    $false = Data::Bool::false;

Returns the canonical "false" value.

This function has a C<()> prototype and works as a constant
(suitable for inlining by the Perl interpreter).

=head2 is_bool

    $is_bool = Data::Bool::is_bool($value);

Returns true if given a C<Data::Bool> boolean.
Returns false otherwise.

This function has a C<($)> prototype.

=head2 to_bool

    $bool = Data::Bool::to_bool($value);

Turns a true or false Perl value into
C<Data::Bool::true> or C<Data::Bool::false>.

This function has a C<($)> prototype.

=head2 true

    $true = Data::Bool::true;

Returns the canonical "true" value.

This function has a C<()> prototype and works as a constant
(suitable for inlining by the Perl interpreter).

=head1 METHODS

The following methods are implemented for L<Data::Bool> booleans.

=head2 new

    $bool = Data::Bool::BOOL_PACKAGE->new($value);

Creates a new L<Data::Bool> boolean from a true or false Perl value.

This is similar to C<Data::Bool::to_bool($value)>, but L</new>
produces independent boolean objects while L</to_bool>
returns the shared L</true> and L</false> instances.

Most of the time this is not needed or desired, except
for a few specialized cases. Prefer L</true>, L</false>, L</to_bool>.

=head1 COMPATIBILITY

Besides the agreement on using C<JSON::PP::Boolean>
as the canonical package for booleans, as of May 2018,
the main JSON modules all step on each others' toes
with varying degrees of intensity when it comes
to decide what is implemented.

What we consider here the "main JSON modules" are:
L<JSON::PP>, L<Types::Serialiser> (used by L<JSON::XS>), and L<Cpanel::JSON::XS>.
These are the original sources of ideas,
implementations, and package names related to boolean
support and the most relevant
CPAN modules on the dependency chain.

For example,

=over 4

=item *

L<JSON::PP::Boolean> defines C<VERSION> and overloads
C<0+>, C<++> and C<--> operators.

=item *

L<Types::Serialiser> establishes an C<ISA> relationship
with a base class overloading the same operators above.

=item *

L<Cpanel::JSON::XS> in turn overloads these operators
and a few more.

=back

As these modules are loaded by a Perl program,
these actions are all replayed over the C<JSON::PP::Boolean>
package. (The last one loaded is basically who wins
on the final code at runtime.) This is almost harmless,
because their implementations mostly agree with each other.
But it is not hard to see this getting out of hand
if they start to diverge.

The additional interface for boolean support provided by these
modules, whether functions, variables and methods, live on their own packages:

=over 4

=item *

L<JSON::PP>: C<true>, C<false>, C<is_bool> under C<JSON::PP>

=item *

L<Types::Serialiser>: C<true>, C<$true>, C<false>, C<$false>, C<is_bool>,
C<is_true>, C<is_false> under C<Types::Serialiser>

=item *

L<Cpanel::JSON::XS>: C<true>, C<$true>, C<false>, C<$false>,
C<is_bool> under C<Cpanel::JSON::XS>.

=back

They all have subtle differences related to prototypes, enabled pragmas,
and stricter or more lenient behavior. Since those don't dwell
in the same base package, C<JSON::PP::Boolean>, they are safe
from the point of view of other consumers of booleans and relevant
to the back-compatibility history of each of these modules.
But they all contribute to the lack of consistency on the interface
to deal with booleans.

Those issues mainly stem from the lack
of a consensus on ownership of C<JSON::PP::Boolean>.

When L<Data::Bool> gets added to this list as yet another
module playing with C<JSON::PP::Boolean>,
it takes the most conservative approach: it does not touch a function
or overloaded method which is already there.
That means it will still provide the entire interface described
before, because it will implement anything that is missing.
But it may suboptimally accept what has been defined before
by a previous module. Moreover it does not fight with modules which
get loaded after either.

The best scenario would be if / when the main JSON modules
delegated the content of the boolean implementation
to a distribution containing L<JSON::PP::Boolean>.
Even if that does not happen,
this module still offers as a compatible and sane interface
to booleans as objects for Perl.

=head1 BUGS

The use of L<overload> makes this module heavier.
See L<Types::Serializer/"BUGS">.

=head1 ACKNOWLEDGMENTS

The original idea and code came from L<JSON::XS::Boolean> written by Marc Lehmann.

Tina Müller inspired me to create this distribution after reading her
L<report on PTS 2018|http://blogs.perl.org/users/tinita/2018/05/my-report-of-the-perl-toolchain-summit-2018-in-oslo.html>.

=head1 DEBUGGING

When L<Data::Bool> gets loaded, it stops L<JSON::PP::Boolean>
module from being loaded, since they are redundant.
This prevents subroutines to be redefined and the
corresponding warnings.
You can set the C<DATA_BOOL_NICE> environment variable to
avoid that behavior.

    DATA_BOOL_NICE=1

You can set the C<DATA_BOOL_LOUD> environment variable to
produce explicit warnings on subroutines found on
L<JSON::PP::Boolean> package that L<Data::Bool> will skip
and accept as they are.

    DATA_BOOL_LOUD=1

=head1 SEE ALSO

L<Types::Serialiser>

L<JSON::PP>

L<JSON::XS>

L<Cpanel::JSON::XS>

=head1 AUTHOR

Adriano Ferreira <ferreira@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2018 by Adriano Ferreira.

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

=cut


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.