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::Comp

VERSION

version 0.0207

DESCRIPTION

Component-oriented programming

A new promising component-oriented programming concepts. 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, component oriented Evo::Comp introduces flexible component roles Evo::Role.

So I decided to write all the stuff and name it component oriented programming. More explanation will be written later

SYNOPSYS

  package main;
  use Evo;

  {

    package My::Human;
    use Evo '-Comp *';

    has 'name' => 'unnamed';
    has 'gender', is => 'ro', required => 1;
    has age => check => sub($v) { $v >= 18 };
  }

  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;

  # 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');

Usage

creating component

Describe a component, use need to import "*" to get it worked

  package My::Comp;
  use Evo '-Comp *';
  has 'simple';

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

new

important!!! Because Components reuse code horizontal, there's no need to pass the class string to every invocation of new (or Evo::Comp::Out/"init"). So instead of My::Class->new use My::Comp::new

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

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

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

Storage

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

Declaring attribute

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

  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.

  # pay attention, no argument passed here
  has ref => sub() { {} };
  has foo => default => sub() { [] };

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 '-Comp *';

    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

Instead of OO inheritance, wich suffers from fragile base class problem, Evo::Comp provides Evo::Role. This is a perfect choice for code reuse because of it's safety and flexibility. Look at Evo::Role for more information

with

Load role and install all methods and attributes to the component. Supports "shortcuts" in Evo

    package Person;
    use Evo '-Comp *';

    with ':LoudHuman', ':Human';

Circular requirements can be solved by requiring roles in the single with. See "requires" in Evo::Role

overrides

  override qw(foo bar);

Mark names as overriden. Use it before "with". You can also use Override attribute, wich is preferred. See "Overriding methods" in Evo::Role

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.