autobox(3pm)

okay bestie, say hi to autobox(3pm) β€” the manual page where native types get to be the main character. πŸ’…

✨ NAME ✨

autobox β€” call methods on native types πŸ¦‹

🌈 SYNOPSIS 🌈

here’s the whole vibe in code, no cap:

        use autobox;

        # integers

            my $range = 10->to(1); # [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]

        # floats

            my $error = 3.1415927->minus(22/7)->abs();

        # strings

            my @list = 'SELECT * FROM foo'->list();
            my $greeting = "Hello, world!"->upper(); # "HELLO, WORLD!"

            $greeting->for_each(\&character_handler);

        # arrays and array refs

            my $schwartzian = @_->map(...)->sort(...)->map(...);
            my $hash = [ 'SELECT * FROM foo WHERE id IN (?, ?)', 1, 2 ]->hash();

        # hashes and hash refs

            { alpha => 'beta', gamma => 'vlissides' }->for_each(...);
            %hash->keys();

        # code refs

            my $plus_five = (\&add)->curry()->(5);
            my $minus_three = sub { $_[0] - $_[1] }->reverse->curry->(3);

        # can, isa, VERSION, import and unimport can be accessed via autobox_class

            42->autobox_class->isa('MyNumber');
            say []->autobox_class->VERSION;

πŸ’– DESCRIPTION πŸ’–

The autobox pragma is literally iconic: it lets methods be called on integers, floats, strings, arrays, hashes, and code references in exactly the same manner as blessed references. periodt. ✨

Autoboxing is transparent: values are not blessed into their (user-defined) implementation class (unless the method elects to bestow such a blessing) β€” they simply use its methods as though they are. 🫢

The classes (packages) into which the native types are boxed are fully configurable. By default, a method invoked on a non-object value is assumed to be defined in a class whose name corresponds to the ref() type of that value β€” or SCALAR if the value is a non-reference.

This mapping can be overridden by passing key/value pairs to the use autobox statement, in which the keys represent native types, and the values their associated classes.

As with regular objects, autoboxed values are passed as the first argument of the specified method. Consequently, given a vanilla use autobox:

"Hello, world!"->upper()

is invoked as:

SCALAR::upper("hello, world!")

while:

[ 1 .. 10 ]->for_each(sub { ... })

resolves to:

ARRAY::for_each([ 1 .. 10 ], sub { ... })

Values beginning with the array @ and hash % sigils are passed by reference, i.e. under the default bindings:

@array->join(', ')
@{ ... }->length()
%hash->keys()
%$hash->values()

are equivalent to:

ARRAY::join(\@array, ', ')
ARRAY::length(\@{ ... })
HASH::keys(\%hash)
HASH::values(\%$hash)

Multiple use autobox statements can appear in the same scope. These are merged both horizontally (i.e. multiple classes can be associated with a particular type) and vertically (i.e. multiple classes can be associated with multiple types).

Thus:

use autobox SCALAR => 'Foo';
use autobox SCALAR => 'Bar';
  • associates SCALAR types with a synthetic class whose @ISA includes both Foo and Bar (in that order).

Likewise:

use autobox SCALAR => 'Foo';
use autobox SCALAR => 'Bar';
use autobox ARRAY  => 'Baz';

and

use autobox SCALAR => [ 'Foo', 'Bar' ];
use autobox ARRAY  => 'Baz';
  • bind SCALAR types to the Foo and Bar classes and ARRAY types to Baz.

autobox is lexically scoped, and bindings for an outer scope can be extended or countermanded in a nested scope:

{
    use autobox; # default bindings: autobox all native types
    ...

    {
        # appends 'MyScalar' to the @ISA associated with SCALAR types
        use autobox SCALAR => 'MyScalar';
        ...
    }

    # back to the default (no MyScalar)
    ...
}

Autoboxing can be turned off entirely by using the no syntax:

{
    use autobox;
    ...
    no autobox;
    ...
}
  • or can be selectively disabled by passing arguments to the no autobox statement:
use autobox; # default bindings

no autobox qw(SCALAR);

[]->foo(); # OK: ARRAY::foo([])

"Hello, world!"->bar(); # runtime error

Autoboxing is not performed for barewords, i.e.

my $foo = Foo->new();

and:

my $foo = new Foo;

behave as expected.

Methods are called on native types by means of the arrow operator. As with regular objects, the right hand side of the operator can either be a bare method name or a variable containing a method name or subroutine reference. Thus the following are all valid:

sub method1 { ... }
my $method2 = 'some_method';
my $method3 = sub { ... };
my $method4 = \&some_method;

" ... "->method1();
[ ... ]->$method2();
{ ... }->$method3();
sub { ... }->$method4();

A native type is only associated with a class if the type => class mapping is supplied in the use autobox statement. Thus the following will not work:

use autobox SCALAR => 'MyScalar';

@array->some_array_method();
  • as no class is specified for the ARRAY type. Note: the result of calling a method on a native type that is not associated with a class is the usual runtime error message:
Can't call method "some_array_method" on unblessed reference at ...

As a convenience, there is one exception to this rule. If use autobox is invoked with no arguments (ignoring the DEBUG option) the four main native types are associated with classes of the same name.

Thus:

use autobox;
  • is equivalent to:
use autobox {
    SCALAR => 'SCALAR',
    ARRAY  => 'ARRAY',
    HASH   => 'HASH',
    CODE   => 'CODE',
}

This facilitates one-liners and prototypes:

use autobox;

sub SCALAR::split { [ split '', $_[0] ] }
sub ARRAY::length { scalar @{$_[0]} }

print "Hello, world!"->split->length();

However, using these default bindings is not recommended as there's no guarantee that another piece of code won't trample over the same namespace/methods. 😭

✨ OPTIONS ✨

okay bestie, so here's where it gets fun πŸ’… you can tell autobox exactly which user-defined class each native type should map to, and you do that by handing use autobox either a hashref or a plain list of key/value pairs. either one, dealer's choice.

here's the full spread of what valid args actually look like, so you know the whole menu:

    use autobox {
        SCALAR    => 'MyScalar'                     # class name
        ARRAY     => 'MyNamespace::',               # class prefix (ending in '::')
        HASH      => [ 'MyHash', 'MyNamespace::' ], # one or more class names and/or prefixes
        CODE      => ...,                           # any of the 3 value types above
        INTEGER   => ...,                           # any of the 3 value types above
        FLOAT     => ...,                           # any of the 3 value types above
        NUMBER    => ...,                           # any of the 3 value types above
        STRING    => ...,                           # any of the 3 value types above
        UNDEF     => ...,                           # any of the 3 value types above
        UNIVERSAL => ...,                           # any of the 3 value types above
        DEFAULT   => ...,                           # any of the 3 value types above
        DEBUG     => ...                            # boolean or coderef
    }

and the tea on those keys β€” INTEGER, FLOAT, NUMBER, STRING, SCALAR, ARRAY, HASH, CODE, UNDEF, DEFAULT and UNIVERSAL β€” is that each one can take three different flavors of value. not two, not four. three. πŸ“

  • A class name, like this:
    use autobox INTEGER => 'MyInt';

this binds that native type to that class, periodt. every method you invoke on values of type key gets dispatched as a method of the class you named in the corresponding value. slay.

  • A namespace: a class prefix (all the way up to and including the final '::') that the specified type name (INTEGER, FLOAT, STRING etc.) gets appended onto. so this:
    use autobox ARRAY => 'Prelude::';

is literally equivalent to:

    use autobox ARRAY => 'Prelude::ARRAY';
  • A reference to an array of class names and/or namespaces β€” bestie you can associate multiple classes with the same specified type. iconic.

🌸 DEFAULT 🌸

so DEFAULT is the vibe-setter: it specifies bindings for any of the four default types (SCALAR, ARRAY, HASH and CODE) that you didn't bother supplying in your use autobox statement. and just like all the other options, the value that goes with the DEFAULT key can be a class name, a namespace, or a reference to an array holding one or more class names and/or namespaces. flexible queen behavior πŸ’…

so this:

    use autobox {
        STRING  => 'MyString',
        DEFAULT => 'MyDefault',
    }

is equivalent to:

    use autobox {
        STRING  => 'MyString',
        SCALAR  => 'MyDefault',
        ARRAY   => 'MyDefault',
        HASH    => 'MyDefault',
        CODE    => 'MyDefault',
    }

which, plot twist, is also equivalent to:

    use autobox {
        INTEGER => 'MyDefault',
        FLOAT   => 'MyDefault',
        STRING  => [ 'MyString', 'MyDefault' ],
        ARRAY   => 'MyDefault',
        HASH    => 'MyDefault',
        CODE    => 'MyDefault',
    }

namespaces inside DEFAULT values get the default type name appended β€” and heads up, for defaulted SCALAR types that appended name is SCALAR, not INTEGER, FLOAT etc. important distinction, don't sleep on it.

so:

    use autobox {
        ARRAY   => 'MyArray',
        HASH    => 'MyHash',
        CODE    => 'MyCode',
        DEFAULT => 'MyNamespace::',
    }

is equivalent to:

    use autobox {
        INTEGER => 'MyNamespace::SCALAR',
        FLOAT   => 'MyNamespace::SCALAR',
        STRING  => 'MyNamespace::SCALAR',
        ARRAY   => 'MyArray',
        HASH    => 'MyHash',
        CODE    => 'MyCode',
    }

and if you wanna opt any of those four default types OUT of defaulting to the DEFAULT value? just feed it a value of undef and you're so done with it:

    use autobox {
        HASH    => undef,
        DEFAULT => 'MyDefault',
    }

    42->foo # ok: MyDefault::foo
    []->bar # ok: MyDefault::bar

    %INC->baz # not ok: runtime error
see that last one? that's a runtime error, bestie. the hash said "not today". 😭

🫧 UNDEF 🫧

meet UNDEF, the pseudotype. it's the one you use to autobox undefined values, because plot twist β€” they do not get autoboxed by default. nope. never. not unless you ask nicely.

this does not work:

    use autobox;

    undef->foo(); # runtime error

this totally works:

    use autobox UNDEF => 'MyUndef';

    undef->foo(); # ok

and so does this:

    use autobox UNDEF => 'MyNamespace::';

    undef->foo(); # ok

look at her go ✨


πŸ‘‘ NUMBER, SCALAR and UNIVERSAL πŸ‘‘

okay so these three β€” NUMBER, SCALAR and UNIVERSAL β€” are the virtual types. they act like macros or shortcuts that spin up bindings for all their subtypes. it's giving "buy one get the whole family". here's the full hierarchy so you can see who's related to who:

  UNIVERSAL -+
             |
             +- SCALAR -+
             |          |
             |          +- NUMBER -+
             |          |          |
             |          |          +- INTEGER
             |          |          |
             |          |          +- FLOAT
             |          |
             |          +- STRING
             |
             +- ARRAY
             |
             +- HASH
             |
             +- CODE

so:

    use autobox NUMBER => 'MyNumber';

is equivalent to:

    use autobox {
        INTEGER => 'MyNumber',
        FLOAT   => 'MyNumber',
    }

and:

    use autobox SCALAR => 'MyScalar';

is equivalent to:

    use autobox {
        INTEGER => 'MyScalar',
        FLOAT   => 'MyScalar',
        STRING  => 'MyScalar',
    }

virtual types can also get passed to unimport through the no autobox syntax, which switches autoboxing OFF for the matching subtypes. so:

    no autobox qw(NUMBER);

is equivalent to:

    no autobox qw(INTEGER FLOAT);

and here's the fun part β€” you can mix virtual type bindings with ordinary bindings for fine-grained control over inheritance and delegation. for instance:

    use autobox {
        INTEGER => 'MyInteger',
        NUMBER  => 'MyNumber',
        SCALAR  => 'MyScalar',
    }

would give you these bindings:

    42->foo              -> [ MyInteger, MyNumber, MyScalar ]
    3.1415927->bar       -> [ MyNumber, MyScalar ]
    "Hello, world!"->baz -> [ MyScalar ]

but wait β€” note that DEFAULT bindings take precedence over virtual type bindings. so:

    use autobox {
        UNIVERSAL => 'MyUniversal',
        DEFAULT   => 'MyDefault', # default SCALAR, ARRAY, HASH and CODE before UNIVERSAL
    }

is equivalent to:

  use autobox {
      INTEGER => [ 'MyDefault', 'MyUniversal' ],
      FLOAT   => [ 'MyDefault', 'MyUniversal' ], # ... etc.
  }

πŸ” DEBUG πŸ”

DEBUG is your little window into what's actually happening β€” it lets you inspect the autobox bindings for the current scope, either by dumping them to the console or by handing them off to a callback function. so you can see the computed bindings in "longhand". very transparent of her πŸ’–

the option gets ignored completely if the value sitting next to the DEBUG key is false.

if the value is a CODE ref, it gets called with a reference to the hash holding the computed bindings for the current scope.

and finally, if DEBUG is true but not a CODE ref, the bindings get dumped to STDERR.

so any of these:

    use autobox DEBUG => 1, ...

or

    use autobox DEBUG => sub { ... }, ...

or

    sub my_callback ($) {
        my $hashref = shift;
        ...
    }

    use autobox DEBUG => \&my_callback, ...

πŸ›  METHODS πŸ› 

import

this method is the one that sets up autobox bindings for the current lexical scope. and it's also how you build autobox extensions β€” i.e. lexically-scoped modules that hand out autobox bindings for one or more native types, all without making the calling code say use autobox at all. sneaky. efficient. queen behavior. πŸ‘‘

how? you subclass autobox and override import. that way your extension effectively translates use MyModule into its own bespoke use autobox call. like so:

    package String::Trim;

    use base qw(autobox);

    sub import {
        my $class = shift;

        $class->SUPER::import(
            STRING => 'String::Trim::String'
        );
    }

    package String::Trim::String;

    sub trim {
        my $string = shift;
        $string =~ s/^\s+//;
        $string =~ s/\s+$//;
        $string;
    }

    1;

heads up on a detail that matters: trim lives in an auxiliary class, not in String::Trim itself β€” and that's on purpose, bestie. it stops String::Trim's own methods (i.e. the ones it inherits from autobox) from getting exposed to STRING types. protect the brand ✨

now the module can be used with no use autobox statement in sight, and trim is live in the current lexical scope:

    #!/usr/bin/env perl

    use String::Trim;

    print "  Hello, world!  "->trim();

🌈 UNIVERSAL METHODS FOR AUTOBOXED TYPES 🌈

autobox_class

autobox adds exactly one method to all autoboxed types, and it's called autobox_class. what's it for? calling UNIVERSAL methods β€” you know the gang: can, DOES, import, isa, unimport and VERSION. like:

    if (sub { ... }->autobox_class->can('curry')) ...
    if (42->autobox_class->isa('SCALAR')) ...
ok and this next bit is the rule, no cap: autobox_class must always be used when you're calling these methods. if you call them straight on native types you get the same results as calling them with autobox turned off. fr fr. like:
    42->isa('NUMBER') # "" (interpreted as "42"->isa("NUMBER"))
    []->can('push')   # Error: Can't call method "can" on unblessed reference

πŸŽ€ EXPORTS πŸŽ€

type

autobox ships with an extra module, autobox::universal, and it exports a single subroutine called type.

this sub returns the type of its argument within autobox β€” which is basically longhand for the type names perl uses internally. autobox uses this value to hook a method invocant up with its designated classes. e.g.

    use autobox::universal qw(type);

    type("42")  # STRING
    type(42)    # INTEGER
    type(42.0)  # FLOAT
    type(undef) # UNDEF

autobox::universal gets loaded automatically by autobox, and β€” as the name is literally telling you β€” you can use it to install a universal type method for your autoboxed values, like:

    use autobox UNIVERSAL => 'autobox::universal';

    42->type        # INTEGER
    3.1415927->type # FLOAT
    %ENV->type      # HASH

πŸ’… and that's the whole toolkit so far β€” more coming, don't go anywhere.

πŸ’€ CAVEATS (read this part bestie, fr fr)

okay so autobox is iconic and all but she's not perfect and u deserve to know the tea before u commit πŸ’…


🌸 Performance

calling

"Hello, world!"->length()

is slightly slower than the equivalent method call on a string-like object, and significantly slower than

length("Hello, world!")

so like… yes she's pretty, but she's got a little overhead, iykyk 😭


πŸ™ˆ Gotchas

Precedence

Due to Perl's precedence rules, some autoboxed literals may need to be parenthesized:

for instance, while this works:

my $curried = sub { ... }->curry();

this doesn't:

my $curried = \&foo->curry();

the solution is to wrap the reference in parentheses:

my $curried = (\&foo)->curry();

the same applies for signed integer and float literals:

# this works
my $range = 10->to(1);

# this doesn't work
my $range = -10->to(10);

# this works
my $range = (-10)->to(10);

so basically: negative numbers and refs gotta be wrapped, no cap πŸŽ€


Perl's special-casing for the print BLOCK ... syntax (see perlsub) means that print { expression() } ... (where the curly brackets denote an anonymous HASH ref) may require some further disambiguation:

# this works
print { foo => 'bar' }->foo();

# and this
print { 'foo', 'bar' }->foo();

# and even this
print { 'foo', 'bar', @_ }->foo();

# but this doesn't
print { @_ }->foo() ? 1 : 0;

in the latter case, the solution is to supply something other than a HASH ref literal as the first argument to print():

# e.g.
print STDOUT { @_ }->foo() ? 1 : 0;

# or
my $hashref = { @_ };
print $hashref->foo() ? 1 : 0;

# or
print '', { @_ }->foo() ? 1 : 0;

# or
print '' . { @_ }->foo() ? 1 : 0;

# or even
{ @_ }->print_if_foo(1, 0);

like… print thinks she's the main character and keeps stealing the curly braces 😭 just give her something else first and she'll chill.


eval EXPR

Like most pragmas, autobox performs operations at compile time, and, as a result, runtime string evals are not executed within its scope, i.e. this doesn't work:

use autobox;

eval "42->foo";

the workaround is to use autobox within the eval, e.g.

eval <<'EOS';
    use autobox;
    42->foo();
EOS

note that the eval BLOCK form works as expected:

use autobox;

eval { 42->foo() }; # OK

so string eval said "not today bestie" but block eval said "periodt, I got u" ✨


Operator Overloading

Operator overloading via the [overload] pragma doesn't (automatically) work. autobox works by lexically overriding the arrow operator. It doesn't bless native types into objects, so overloading β€” or any other kind of "magic" which depends on values being blessed β€” doesn't apply. πŸ’”


⭐ VERSION

3.0.2


πŸ¦‹ SEE ALSO

  • autobox::Core
  • Moose::Autobox
  • perl5i
  • Scalar::Properties

go read these too, they're all part of the girlies πŸ’–


πŸ‘‘ AUTHOR

chocolateboy <chocolate@cpan.org>


Copyright (c) 2003-2024 by chocolateboy.

This library is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0 <https://www.opensource.org/licenses/artistic-license-2.0.php>.


and that's the whole page bestie!! 🫢 this was perl v5.42.3, dated 2026-08-20, and she's still serving autobox(3pm) realness. go write something cursed and beautiful with her πŸ’…βœ¨πŸŽ€

πŸ“¦ where this page comes from