The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Evo::Class

VERSION

version 0.0217

DESCRIPTION

Mixins programming

A new promising inject-code programming concepts based on mixins. Documentation will be available if someone will be willing to help write it.

Why not OO and Moose like?

The most problems with Moose like modules is with initialization. Let's take a look:

  package My::Moose;
  use Moose;
  has foo => is =>'rw';

  package main;
  my $obj = My::Moose->new(fo => 3);
  print $obj->foo;

As you can see, we passed fo instead of foo, and Moose silently ignored it. You should write a huge amout of extra tests to pervent such errors.

Also traditional OO programming suffers the fragile base class problems (use Google). To solve it, injection based Evo::Class introduces flexible roles Evo::Role.

SYNOPSYS

  package main;
  use Evo;

  {

    package My::Human;
    use Evo -Class, -Loaded;

    has 'name' => 'unnamed';
    has 'gender', is => 'ro', required => 1;
    has age => check => sub($v) { $v >= 18 };
    sub greet($self) : Public { say "I'm " . $self->name }
  }

  my $alex = My::Human->new(gender => 'male');

  # default value "unnamed"
  say $alex->name;

  # fluent design
  $alex->name('Alex')->age(18);
  say $alex->name, ': ', $alex->age;

  # method
  $alex->greet;

  ## ------------ protecting you from errors, uncomment to test
  ## will die, gender is required
  #My::Human->new();

  ## will die, age must be >= 18
  #My::Human->new(age => 17, gender => 'male');
  #My::Human->new()->age(17, gender => 'male');

  # --------- code reuse
  {

    package My::Developer;
    use Evo -Class;
    with 'My::Human'; # extends 'My::Human'; implements 'My::Human';

    has lang => 'Perl';

    sub show($self) : Public {
      $self->greet();
      say "I like ", $self->lang;
    }


  }

  my $dev = My::Developer->new(gender => 'male');
  $dev->show;

Usage

creating an object

  package My::Class;
  use Evo -Class;
  has 'simple';

You don't need to call something like __PACKAGE__->meta->make_immutable unlike in Moose, Evo objects are fast enough by design.

new

  my $foo = My::Class->new(simple => 1);
  my $foo2 = My::Class->new();

We're protected from common mistakes, because constructor won't accept unknown attributes

  my $foo = My::Class->new(SiMple => 1);

Storage

The big advantage of Evo object that it's not tied with implementation. The default uses hashes Evo::Class::Hash, but you can easily switch for example to Evo::Class::Out and use any other refs

Declaring attribute

  package My::Foo;
  use Evo '-Class *';

  has 'simple';
  has 'short' => 'value';
  has 'foo' => default => 'value', is => 'rw', check => sub {1};

Syntax

Simple rw attribute

  has 'simple';
  # has 'simple', is => 'rw';

Attribute with default value: short form

  has 'short' => 'value';
  # has 'short', default => 'value';

Full form

  has 'foo' => default => 'value', is => 'rw', check => sub {1};

Options

is

Can be 'rw' or 'ro'; Unlike Perl6 is 'rw' by default

default

Attribute will be filled with this value if isn't provided to the new constructor You can't use references, but you can provide a coderef instead of value, in this case return value of an invocation of this function will be used.

  has ref => sub(%build_args) { {} };
  has foo => default => sub(%build_args) { [] };

This is a good way to init some attribute that should always exists. Arguments, passed to new or init will be passed to the function without object itself (because there are no object yet). If you're expecting another behaviour, check "lazy"

lazy

Like default, but will be filled at the first invocation, not in constructor, and an instance will be passed as the argument

  # pay attention, an instance is passed
  has foo => lazy => sub($self) { [] };

required

Attributes with this options are required

check

You can provide function that will check passed value (via constuctor and changing), and if that function doesn't return true, an exception will be thrown.

  has big => check => sub { shift > 10 };

You can also return (0, "CustomError") to provide more expressive explanation

  package main;
  use Evo;

  {

    package My::Foo;
    use Evo '-Class *';

    has big => check => sub($val) { $val > 10 ? 1 : (0, "not > 10"); };
  };

  my $foo = My::Foo->new(big => 11);

  $foo->big(9);    # will die
  my $bar = My::Foo->new(big => 9);    # will die

CODE REUSE

Method should be marked with :Public if you want to reuse them; Attributes are all public;

Overriding

Evo protects you from method clashing. But if you want to override method or fix clashing, use "has_overriden" function or :Override attribute

    package My::Peter;
    use Evo -Class;
    with 'My::Human';

    has_overriden name => 'peter';
    sub greet : Overriden { }

This differs from traditional OO style. With compoment programming, you should reuse code via Evo::Class::Role or just organize classes with independed pieces of code like "mixing". So, try to override less

extends

Extends classes or roles

implements

Check if all required methods are implemented. Like interfaces

with

This does "extend + check implementation". Consider this example:

  package main;
  use Evo;

  {

    package My::Role::Happy;
    use Evo -Class, -Loaded;

    requires 'name';

    sub greet($self) : Public {
      say "My name is ", $self->name, " and I'm happy!";
    }

    package My::Class;
    use Evo -Class;

    has name => 'alex';

    #extends 'My::Role::Happy';
    #implements 'My::Role::Happy';
    with 'My::Role::Happy';

  }

  My::Class->new()->greet();

My::Role::Happy requires name in derivered class. We could install shared code with extends and then check implemantation with implements. Or just use with wich does both.

You may want to use extends and implements separately to resolve circular requirements, for example

CODE ATTRIBUTES

  sub foo : Override { 'OVERRIDEN'; }

Mark name as overriden. See "Overriding methods" in Evo::Role

AUTHOR

alexbyk.com

COPYRIGHT AND LICENSE

This software is copyright (c) 2016 by alexbyk.

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