Mite: an OO compiler for Perl
Tags: perl, oop, programming, tooling
Published: 2022-07-02 17:38:11

[Moose](https://metacpan.org/pod/Moose) is great, but it does introduce a slight performance hit to your code. In the more than 15 years since it was first released, hardware improvements have made this less of a problem than it once was. Even so, if performance is a concern for your project, Moose might not be what you want. It also has a fairly big collection of non-core dependencies.

[Moo](https://metacpan.org/pod/Moo) is a lighter weight version, minus with meta-object protocol, but supporting nearly all of Moose's other features. It loads faster, sometimes runs faster, and has fewer dependencies. (And most of the dependencies it does have are just modules which used to be part of Moo but were split out into separate distributions.)

But what if you could have fast Moose-like object-oriented code without the dependencies?

In 2013, Michael Schwern started work on [Mite](https://metacpan.org/pod/Mite) to do just that. It was abandoned in 2014, but I've taken it over and expanded the feature set to roughly equivalent to Moo.

Mite is an object-oriented programming _compiler_ for Perl. It allows you to write familiar Moose-like object-oriented code, then compile that into plain Perl with zero non-core dependencies. Your compiled code does not even have a dependency on Mite itself!

How do I use Mite?
------------------

Here's how you could start a project with Mite or port an existing Moose/Moo project.

```bash
   cd Your-Project/
   mite init 'Your::Project'
   mite compile
```

After you've run those commands, Mite will create a module called `Your::Project::Mite`. This module is your project's own little gateway to Mite. This module is called the shim.

Now let's write a test case:

```perl
   # t/unit/Your-Project-Widget.t
   use Test2::V0
      -target => 'Your::Project::Widget';
   
   can_ok( $CLASS, 'new' );
   
   my $object = $CLASS->new( name => 'Quux' );
   isa_ok( $object, $CLASS );
   
   subtest 'Method `name`' => sub {
      can_ok( $object, 'name' );
      is( $object->name, 'Quux', 'expected value' );
      
      my $exception = dies {
         $object->name( 'XYZ' );
      };
      isnt( $exception, undef, 'read-only attribute' );
   };
   
   subtest 'Method `upper_case_name`' => sub {
      can_ok( $object, 'upper_case_name' );
      is( $object->upper_case_name, 'QUUX', 'expected value' );
   };
   
   done_testing;
```

And a class to implement the functionality:

```perl
   # lib/Your/Project/Widget.pm
   package Your::Project::Widget;
   use Your::Project::Mite;
   
   has name => (
      is     => 'ro',
      isa    => 'Str',
   );
   
   sub upper_case_name {
      my $self = shift;
      return uc( $self->name );
   }
   
   1;
```

Run `mite compile` again then run the test case. It should pass.

How does Mite work?
-------------------

It's important to understand what Mite is doing behind the scenes.

When you ran `mite compile`, Mite created a file called _lib/Your/Project/Widget.pm.mite.pm_. (Yes, a triple file extension!) This file contains your class's `new` method. It contains the code for the accessor.

That file does not contain the code for `upper_case_name` which is still in the original _lib/Your/Project/Widget.pm_.

When Perl loads `Your::Project::Widget`, it will see this line and load the shim:

```perl
   use Your::Project::Mite;
```

The shim just loads _lib/Your/Project/Widget.pm.mite.pm_, exports a `has` function that does (almost) nothing, and then gets out of the way. This gives Perl a working class.

What features does Mite support?
--------------------------------

Most of what Moo supports is supported by Mite. In particular:

### `extends @superclasses`

Mite classes within your project can inherit from other Mite classes within your project, but not from non-Mite classes, and not from Mite classes from a different project.

### `with @roles`

As of version 0.002000, Mite also supports roles. If you want your package to be a role instead of a class, just do:

```perl
   package Your::Project::Nameable;
   use Your::Project::Mite -role;
   has name => (
      is => 'ro',
      isa => 'Str',
   );
   1;
```

As with `extends`, a limitation is that you can only use Mite roles from within your own project, not non-Mite roles, nor Mite roles from a different project.

(A future development might add support for [Role::Tiny](https://metacpan.org/pod/Role%3A%3ATiny) roles though.)

### `has $attrname => %spec`

Attributes are obviously one of the main features people look for in a Perl object-oriented programming framework and Mite supports nearly all of Moose's features for defining attributes.

This includes `is => 'ro'`, `is => 'rw'`, `is => 'bare'`, `is => 'rwp'` (like Moo), and `is => 'lazy'` (like Moo); `required` and `init_arg` for attribute initialization; `reader`, `writer`, `accessor`, `predicate`, `clearer`, and `trigger`; `lazy`, `default`, and `builder`; `weak_ref`; `isa` and `coerce` for type constraints, including support for any type constraints in [Types::Standard](https://metacpan.org/pod/Types%3A%3AStandard), [Types::Common::Numeric](https://metacpan.org/pod/Types%3A%3ACommon%3A%3ANumeric), and [Types::Common::String](https://metacpan.org/pod/Types%3A%3ACommon%3A%3AString); and delegation using `handles`. It also supports an option which Moose doesn't provide: `alias` for aliasing attributes.

Mite builds in the functionality of [MooseX::StrictConstructor](https://metacpan.org/pod/MooseX%3A%3AStrictConstructor), dying with an appropriate error message if you pass your class's constructor any parameters it wasn't expecting.

### `BUILDARGS`, `BUILD`, and `DEMOLISH`

Methods you can define to control the life cycle of objects.

### `before $method => sub { ... }`

### `after $method => sub { ... }`

### `around $method => sub { ... }`

Mite classes and roles can define method modifiers.

As long as your needs aren't super-sophisticated (introspection using the MOP, runtime application of roles, etc), Mite probably has the features you need for even medium to large projects.

Mite itself uses Mite!

Be honest, what are the drawbacks?
----------------------------------

This code still doesn't have a lot of testing "in the wild". Moose and Moo have proven track records.

You need to remember to `mite compile` your code after making changes before running your test suite or packaging up a release. This can be annoyingly easy to forget to do. (Though Mite does also include extensions for ExtUtils::MakeMaker and Module::Build to help integrate that into your workflow.)

The Mite compiler's scope of only looking at the files within your own project limits the ability to create roles which can be composed by third-parties, or classes which can easily be extended by third-parties. If you want that, Moose or Moo are a better option.

Okay, I'm interested
--------------------

If you've read this and you're thinking about porting a Moose or Moo project to Mite, feel free to @-mention [tobyink on Github](https://github.com/tobyink) in issue tickets, pull requests, etc if you need any help.

If there are features which you think Mite is missing which you'd need to port your project to Mite, file bugs with the [Mite issue tracker](https://github.com/tobyink/p5-mite/issues).