Evo::Class
version 0.0229
A new promising inject-code programming concepts based on mixins. Documentation will be available if someone will be willing to help write it.
The main difference is Evo stores attributes outside the object, so any ref could be an object, while Moose allow you to use only hashes. This makes possible, for example, to avoid delegating $stream->fh and makes a code faster. Also avoiding hashes improves performance (Evo::Class with XS backend should be 50-100% faster than similar hash-based modules). (see https://github.com/alexbyk/perl-evo/tree/master/bench)
Evo
$stream->fh
The syntax differs from Moose too, I fixed most frustating parts of it. It's not Moose-compatible at all. Evo::Class is more strict by default and prevents many errors.
Every class is a role (Evo::Class::Role) and we don't use perl's @ISA OO inheritance. Code reuse is based on so called "mixins". This concept doesn't suffer a fragile base class problem from traditional OO
Evo::Class::Role
@ISA
fragile base class problem
Every class is also an interface and can be used to check the shape of other classes.
A tiny amount of code means less bugs.
These advantages make Evo::Class perfect for both "corporate level" and "small" projects
Every class can be a Role. Every Class/Role can be an Interface. This means you can extend class with roles, roles with roles, ioc with hash-classes and so on.
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) { 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) { $self->greet(); say "I like ", $self->lang; } } my $dev = My::Developer->new(gender => 'male'); $dev->show;
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.
__PACKAGE__->meta->make_immutable
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. You may think why not My::Class::new? You're right. The first option isn't really necessary and even constructor doesn't use it at all. But I decided to leave it that way because many developers are familiar with My::Class->new form. There is also an "init" function for perfectionists
My::Class::new
My::Class->new
Return current Evo::Class::Meta object for the class
The big advantage of Evo object that it's not tied with hashes. You can use init to bless and init any reference.
init
package My::Obj; use Evo 'Evo::Class *, -new'; # load all except "new" sub new ($me, %opts) { $me->init([], %opts); } package main; use Evo; say My::Obj->new();
package My::Foo; use Evo '-Class *'; has 'simple'; has 'short' => 'value'; has 'foo' => default => 'value', is => 'rw', check => sub {1};
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};
Can be 'rw' or 'ro'; Unlike Perl6 is 'rw' by 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.
new
has ref => sub($class) { {} }; has foo => default => sub($class) { [] };
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"
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) { [] };
You should now that using this feature is an antipattern in the mose of the cases. "default" is preferable if you're not sure
has 'foo', required => 1;
Attributes with this options are required
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
(0, "CustomError")
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
All methods, defined in a class (not imported) are public. Functions, imported from other modules, don't become public and don't make a mess.
All attributes are public.
Methods, generated somehow else, for example by *foo = sub {}, can be marked as public by "reg_method" in Evo::Class::Meta
*foo = sub {}
If you want to mark a method as private, use new lexical_subs feature
lexical_subs
my sub private {'private'}
You can also use "mark_as_private" in Evo::Class::Meta
Evo protects you from method clashing. But if you want to override method or fix clashing, use "has_over" function or :Override attribute
:Override
package My::Peter; use Evo -Class; with 'My::Human'; has_over name => 'peter'; sub greet : Over { }
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 classes or roles
Check if all required methods are implemented. Like interfaces
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) { 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.
My::Role::Happy
name
extends
implements
with
You may want to use extends and implements separately to resolve circular requirements, for example
sub foo : Over { 'OVERRIDEN'; }
Mark name as overridden. See "Overriding methods" in Evo::Role
alexbyk.com
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.
To install Evo, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Evo
CPAN shell
perl -MCPAN -e shell install Evo
For more information on module installation, please visit the detailed CPAN module installation guide.