Type::T...thOther(3)

πŸ’– Type::Tiny::Manual::UsingWithOther πŸ’–

okay so bestie, real talk: this one lives in the User Contributed Perl Documentation manual set, and it's all about using Type::Tiny with Class::InsideOut, Params::Check, and Object::Accessor. Buckle up, it's giving ✨ niche but useful ✨.


🌸 NAME 🌸

Type::Tiny::Manual::UsingWithOther β€” using Type::Tiny with Class::InsideOut, Params::Check, and Object::Accessor.


πŸ¦‹ MANUAL πŸ¦‹

The antlers crew aren't the only object-oriented programming toolkits in Perl town, bestie. Type::Tiny might have been built with Moose, Mouse, and Moo in mind, but it can absolutely be used with other toolkits too. 🫢

These toolkits are... well... hmm... okay... they exist. 😭

Lowkey though: if you're starting a new project, there's very little reason not to use Class::Tiny, Moo, or Moose. So you're probably okay to skip this whole part of the fine manual and go straight to Type::Tiny::Manual::UsingWithTestMore. Periodt. πŸ’…

πŸŽ€ Class::InsideOut πŸŽ€

You want Class::InsideOut 1.13 or above, because that version has support for blessed and overloaded objects (including Type::Tiny type constraints, iconic behavior) for the "get_hook" and "set_hook" options. No cap. πŸ’–

package Person {
  use Class::InsideOut qw( public );
  use Types::Standard qw( Str Int );
  use Types::Common::Numeric qw( PositiveInt );
  use Type::Params qw( signature_for );

  # Type checks are really easy.
  # Just supply the type as a set hook.
  public name => my %_name, {
    set_hook => Str,
  };

  # Define a type that silently coerces negative values
  # to positive. It's silly, but it works as an example!
  my $Years = PositiveInt->plus_coercions(Int, q{ abs($_) });

  # Coercions are more annoying, but possible.
  public age => my %_age, {
    set_hook => sub { $_ = $Years->assert_coerce($_) },
  };

  # Parameter checking for methods is as expected.
  signature_for get_older => ( method => 1, positional => [ $Years ] );

  sub get_older ( $self, $years ) {
    $self->_set_age( $self->age + $years );
  }
}

🌈 Params::Check and Object::Accessor 🌈

The Params::Check allow() function, the "allow" option for the Params::Check check() function, and the input validation mechanism for Object::Accessor all work the same way, which is basically a limited pure-Perl implementation of the smart match operator. It doesn't directly support Type::Tiny constraints, but it does support coderefs β€” so you can use Type::Tiny's "compiled_check" method to obtain a suitable coderef. Slay. ✨

Param::Check example:

my $tmpl = {
  name => { allow => Str->compiled_check },
  age  => { allow => Int->compiled_check },
};
check($tmpl, { name => "Bob", age => 32 })
  or die Params::Check::last_error();

Object::Accessor example:

my $obj = Object::Accessor->new;
$obj->mk_accessors(
  { name => Str->compiled_check },
  { age  => Int->compiled_check },
);
⚠️ Caveat, bestie, pay attention: Object::Accessor doesn't die when a value fails to meet its type constraint; instead it outputs a warning to STDERR. This behaviour can be changed by setting "$Object::Accessor::FATAL = 1". πŸ’€

🧚 Class::Struct 🧚

This is a proof-of-concept of how Type::Tiny can be used to constrain attributes for Class::Struct. It's probably not a good idea to use this in production, because it slows down "UNIVERSAL::isa" globally. Like, globally globally. 😭

use Types::Standard -types;
use Class::Struct;

{
  my %MAP;
  my $orig_isa = \&UNIVERSAL::isa;
  *UNIVERSAL::isa = sub {
    return $MAP{$1}->check($_[0])
      if $_[1] =~ /^CLASSSTRUCT::TYPETINY::(.+)$/ && exists $MAP{$1};
    goto $orig;
  };
  my $orig_dn = \&Type::Tiny::display_name;
  *Type::Tiny::display_name = sub {
    if (caller(1) eq 'Class::Struct') {
      $MAP{$_[0]{uniq}} = $_[0];
      return "CLASSSTRUCT::TYPETINY::".$_[0]{uniq};
    }
    goto $orig_dn;
  };
}

struct Person => [ name => Str, age => Int ];

my $bob = Person->new(
  name => "Bob",
  age  => 21,
);

$bob->name("Robert");   # okay
$bob->name([]);         # dies

πŸ’… Class::Plain πŸ’…

There is not currently a high level of integration, but here's a quick example of type checking attributes in the constructor. 🫢

Heads up: if any of your accessors are ":rw" then you would also need to add type checks to those. iykyk.

use Class::Plain;

class Point {
  use Types::Common -types, -sigs;

  field x :reader;
  field y :reader;

  signature_for new => (
    method => !!1,
    bless  => !!0,
    named  => [
      x => Int,
      y => Int,
    ],
  );

  method as_arrayref () {
    return [ $self->x, $self->y ];
  }
}

The following signature may also be of interest:

signature_for new => (
  method   => !!1,
  multiple => [
    {
      named => [
        x => Int,
        y => Int,
      ],
      bless => !!0,
    },
    {
      positional => [ Int, Int ],
      goto_next  => sub {
        my ( $class, $x, $y ) = @_;
        return ( $class, { x => $x, y => $y } ),
      },
    },
  ],
);

This would allow your class to be instantiated using any of the following:

my $point11 = Point->new( { x => 1, y => 1 } );
my $point22 = Point->new(   x => 2, y => 2   );
my $point33 = Point->new( 3, 3 );

⭐ NEXT STEPS ⭐

Here's your next step, bestie:

  • Type::Tiny::Manual::UsingWithTestMore β€” Type::Tiny for test suites. 🌸

πŸ‘‘ AUTHOR πŸ‘‘

Toby Inkster <tobyink@cpan.org>.


This software is copyright (c) 2013-2014, 2017-2025 by Toby Inkster.

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


🚨 DISCLAIMER OF WARRANTIES 🚨

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.


and that's the whole page, bestie!! πŸ¦‹βœ¨ this one came from perl v5.42.0, dated 2026-01-17 β€” go forth and type-check responsibly πŸ’–πŸŽ€πŸ’…

πŸ“¦ where this page comes from