The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

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

VERSION

version 2.98014

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()

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 1 or 0 blessed into JSON::PP::Boolean which was chosen as the canonical package for these objects.

The problem with that was the coupling with 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 Types::Serialiser approached this problem by creating a common interface used by JSON::XS and 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 JSON::PP::Boolean by making the Data::Bool implementation stash an alias for 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'

INTERFACE

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

    Data::Bool::true()

    Data::Bool::false()

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

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

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

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

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

Converting from a Perl true or false value into 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 Data::Bool booleans.

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

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

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

BASIC CODE

The code of this module would look as below, if not for the efforts to play nice with JSON::PP, Types::Serialiser and 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 }

FUNCTIONS

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

BOOL_PACKAGE

    $package = Data::Bool::BOOL_PACKAGE;

The implementation package of the boolean objects.

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

false

    $false = Data::Bool::false;

Returns the canonical "false" value.

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

is_bool

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

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

This function has a ($) prototype.

to_bool

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

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

This function has a ($) prototype.

true

    $true = Data::Bool::true;

Returns the canonical "true" value.

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

METHODS

The following methods are implemented for Data::Bool booleans.

new

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

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

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

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

COMPATIBILITY

Besides the agreement on using 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: JSON::PP, Types::Serialiser (used by JSON::XS), and 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,

As these modules are loaded by a Perl program, these actions are all replayed over the 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:

  • JSON::PP: true, false, is_bool under JSON::PP

  • Types::Serialiser: true, $true, false, $false, is_bool, is_true, is_false under Types::Serialiser

  • Cpanel::JSON::XS: true, $true, false, $false, is_bool under Cpanel::JSON::XS.

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, 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 JSON::PP::Boolean.

When Data::Bool gets added to this list as yet another module playing with 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 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.

BUGS

The use of overload makes this module heavier. See "BUGS" in Types::Serializer.

ACKNOWLEDGMENTS

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

Tina Müller inspired me to create this distribution after reading her report on PTS 2018.

DEBUGGING

When Data::Bool gets loaded, it stops 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 DATA_BOOL_NICE environment variable to avoid that behavior.

    DATA_BOOL_NICE=1

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

    DATA_BOOL_LOUD=1

SEE ALSO

Types::Serialiser

JSON::PP

JSON::XS

Cpanel::JSON::XS

AUTHOR

Adriano Ferreira <ferreira@cpan.org>

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.