Module:...ic::Null(3)

πŸ“¦ version 1.1.3, in alpine 3.24

πŸ¦‹ Module::Generic::Null πŸ¦‹

okay bestie, this is from the User Contributed Perl Documentation section β€” page Module:...ic::Null(3) β€” and it’s literally about the Null Value Chaining Object Class. yes, a null object that still lets you chain methods. iconic behavior fr fr πŸ’…


πŸ’– NAME πŸ’–

Module::Generic::Null β€” Null Value Chaining Object Class

✨ SYNOPSIS ✨

# In your code:
sub customer
{
    my $self = shift( @_ );
    return( $self->error( "No customer id was provided" ) ) if( !scalar( @_ ) );
    return( $self->customer_info_to_object( @_ ) );
}

# And this method is called without providing an id, thus triggering an error,
# but is chained. Upon error triggered by method "error", a Module::Generic::Null
# object is returned
my $name = $object->customer->name;

πŸŽ€ VERSION πŸŽ€

v1.1.4

🌸 DESCRIPTION 🌸

Normally the call above would have triggered a perl error like "Cannot call method name on an undefined value", but since error in Module::Generic returns a Module::Generic::Null object, the method name here is called without triggering an error, and returns the right value based on the expectation of the caller which will ultimately result in undef in scalar context or an empty list in list context. no cap, that’s the whole vibe πŸ’«

Module::Generic::Null uses AUTOLOAD to allow any method to work in chaining, but contains the original error within its object.

When the AUTOLOAD is called, it checks the call context and returns the appropriate value (self object, code ref, hash ref, array ref, scalar ref, or simply undef or empty list)

If the caller wants an hash reference, it returns an empty hash reference.

If the caller wants an array reference, it returns an empty array reference.

If the caller wants a scalar reference, it returns a scalar reference pointing to undef.

If the caller wants a code reference, it returns an anonymous subroutine that returns undef or an empty list.

If the caller is calling another method right after, this means this is an object context and Module::Generic::Null will return the current object it was called with.

In any other context, undef is returned or an empty list.

Without using Module::Generic::Null, if you return simply undef, like:

my $val = $object->return_false->[0];

sub return_false{ return }

The above would trigger an error that the value returned by return_false is not an array reference. Instead of having the caller checking what kind of returned value was returned, the caller only need to check if it is defined or not, no matter the context in which it is called.

For example:

my $this = My::Object->new;
my $val  = $this->call1;
defined( $val ) || die( $this->error );
# return undef)

# object context
$val = $this->call1->call_again;
defined( $val ) || die( $this->error );
# $val is undefined

# hash reference context
$val = $this->call1->fake->{name};
defined( $val ) || die( $this->error );
# $val is undefined

# array reference context
$val = $this->call1->fake->[0];
defined( $val ) || die( $this->error );
# $val is undefined

# code reference context
$val = $this->call1->fake->();
defined( $val ) || die( $this->error );
# $val is undefined

# scalar reference context
$val = ${$this->call1->fake};
defined( $val ) || die( $this->error );
# $val is undefined

# simple scalar
$val = $this->call1->fake;
defined( $val ) || die( $this->error );
# $val is undefined

package My::Object;
use parent qw( Module::Generic );

sub call1
{
    return( shift->call2 );
}

sub call2 { return( shift->new_null ); }

sub call_again
{
    my $self = shift( @_ );
    print( "Got here in call_again\n" );
    return( $self );
}

Undefined value is the typical response one gets when an error occurred, so you can check like this, assuming you know you normally would not get a false value :

my $name = $object->customer->name || die( $object->error );

Apart from that, this does not do anything meaningful. it’s giving β€œvery demure, very mindful, very null” 🫢

πŸ”₯ METHODS πŸ”₯

There is only 1 method. This module makes it possible to call it with any method to fake original data flow.

🐚 new

This takes an optional error object (e.g., a Module::Generic::Exception) and an optional hash or hash reference of options, and returns a new Module::Generic::Null object.

Possible options:

  • wants β€” Specifies the desired context for the null object to return. Can be any of (case-insensitive): ARRAY, BOOLEAN, CODE, GLOB, HASH, LIST, OBJECT, REFSCALAR, SCALAR, VOID. If not specified, the context is determined dynamically using Wanted.

Example:

my $null = Module::Generic::Null->new( wants => 'HASH' );
my $hash = $null->fake; # Returns {}

πŸŽ€ SERIALISATION πŸŽ€

Serialisation by CBOR, Sereal and Storable::Improved (or the legacy Storable) is supported by this package. To that effect, the following subroutines are implemented: FREEZE, THAW, STORABLE_freeze and STORABLE_thaw

πŸ”₯ THREAD & PROCESS SAFETY πŸ”₯

Module::Generic::Null is fully thread-safe and process-safe, designed to provide a null object for method chaining without breaking in multi-threaded or multi-process environments, such as Perl ithreads or mod_perl’s threaded Multi-Processing Modules (MPMs) like Worker or Event. All operations are per-object, ensuring no shared state conflicts. slay πŸ’š

Key considerations for thread and process safety:

  • Object State β€” All methods, including new, AUTOLOAD, and serialisation subroutines (FREEZE, THAW), operate exclusively on the object’s internal hash, which is unique to each instance. This ensures thread-safe and process-safe behaviour without shared resources:
use threads;
my $null = Module::Generic::Null->new( wants => 'HASH' );
my @threads = map
{
    threads->create(sub
    {
        my $val = $null->fake; # Returns empty hashref, thread-safe
        return( ref( $val ) eq 'HASH' ? 1 : 0 );
    });
} 1..5;
$_->join for @threads;
  • AUTOLOAD β€” The AUTOLOAD mechanism handles arbitrary method calls, returning context-appropriate values (e.g., empty hashref, arrayref, or the object itself) based on Wanted’s stack inspection. This process is thread-safe, as it uses no shared state and creates per-object or immutable return values:
my $null = Module::Generic::Null->new;
my $val = $null->any_method->another; # Returns $null, thread-safe
  • Overloading β€” Overloaded operators (stringification, equality, boolean) are pure functions operating on the object’s state or local data, ensuring thread-safety:
my $null = Module::Generic::Null->new;
my $str = "$null"; # Empty string, thread-safe
my $bool = $null ? 1 : 0; # False, thread-safe
  • Serialisation β€” Serialisation methods (FREEZE, THAW) copy the object’s hash, which is per-object and thread-safe, supporting CBOR::XS, Sereal, and Storable::Improved:
use threads;
use Sereal;
my $null = Module::Generic::Null->new;
my $encoded = Sereal::encode_sereal( $null ); # Thread-safe
  • mod_perl Considerations β€”
    • Prefork MPM β€” Each process creates independent Module::Generic::Null instances, ensuring process isolation with no shared state.
    • Threaded MPMs (Worker/Event) β€” All operations remain thread-safe due to per-object state and thread-safe dependencies (Scalar::Util, Wanted). No thread-unsafe Perl functions (e.g., localtime in perlfunc) are used. Users should ensure mod_perl handlers clean up objects to prevent memory leaks, though Module::Generic::Null itself requires no special handling. Consult perlthrtut (http://perldoc.perl.org/perlthrtut.html) and mod_perl documentation (https://perl.apache.org/docs/2.0/user/coding/coding.html#Thread_environment_Issues) for details.
  • Process Safety β€” Since all operations are per-object and use no system-level resources (e.g., files, sockets), Module::Generic::Null is inherently process-safe. Separate processes create distinct instances with no interaction:
use POSIX qw( fork );
my $pid = fork();
if( $pid == 0 )
{
    my $null = Module::Generic::Null->new;
    my $val = $null->fake; # Safe, per-process state
    exit;
}
waitpid( $pid, 0 );

For debugging in threaded or multi-process environments, use platform-specific commands (e.g., on Linux):

ls -l /proc/$$/fd  # List open file descriptors

See your operating system’s documentation for equivalent commands.

🌈 CREDITS 🌈

Based on an original idea from Brian D. Foy (https://stackoverflow.com/users/2766176/brian-d-foy) discussed on StackOverflow (https://stackoverflow.com/a/7068271/4814971) and also on Perl Monks (https://www.perlmonks.org/?node_id=265214)

πŸ‘‘ AUTHOR πŸ‘‘

Jacques Deguest <jack@deguest.jp>

Copyright (c) 2000-2024 DEGUEST Pte. Ltd.

You can use, copy, modify and redistribute this package and associated files under the same terms as Perl itself.


okay bye bestie πŸ’‹ this page came from perl v5.42.2, dated 2026-02-15, and yes it’s the Module:...ic::Null(3) page β€” mwah! 🫢✨

πŸ“¦ where this page comes from