Slide 1

Slide 1 text

Interfaces in Perl5 kobaken a.k.a @kfly8 The Perl Conference 2019 #TPCiP

Slide 2

Slide 2 text

me • kobaken a.k.a @kfly8 • leader Gotanda.pm • orgnize YAPC::Tokyo 2019

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

I ❤ Perl

Slide 12

Slide 12 text

Interfaces in Perl5

Slide 13

Slide 13 text

DBI

Slide 14

Slide 14 text

my $dbh = DBI->connect(...); my $items = $dbh->selectrow_arrayref(…);

Slide 15

Slide 15 text

Unified Interface independent of datastore

Slide 16

Slide 16 text

Unified Interface provides values

Slide 17

Slide 17 text

Limitation makes value

Slide 18

Slide 18 text

Agenda 1.DEMO & Features 2.Goal 3.Example: TODO app 4.Internal 5.Performance

Slide 19

Slide 19 text

Function::Interface is Java-like interface

Slide 20

Slide 20 text

DEMO

Slide 21

Slide 21 text

Features of Function::Interface 1.Typed interface definition 2.Check at compile time

Slide 22

Slide 22 text

What is Goal ?

Slide 23

Slide 23 text

To maintain large Perl apps

Slide 24

Slide 24 text

Interface is good for large apps

Slide 25

Slide 25 text

1. Be easier to fix implementation

Slide 26

Slide 26 text

2. Stable dependencies

Slide 27

Slide 27 text

Goal • To maintain large apps. • Interface is good for large apps 1. Easy to fix implementation 2. Stable Dependencies

Slide 28

Slide 28 text

Example: TODO app

Slide 29

Slide 29 text

Case: “Inheritance”

Slide 30

Slide 30 text

package TodoRepository; use parent qw(DBI); sub new { my $class = shift; $class->connect(…); }

Slide 31

Slide 31 text

package TodoRepository; … 
 sub select { my ($self, $user_id) = @_; $self->selectall_arrayref( ‘SELECT * FROM todos WHERE user_id = ?’, $user_id ); }

Slide 32

Slide 32 text

package TodoApp; use TodoRepository; my $repo = TodoRepository->new;

Slide 33

Slide 33 text

package TodoApp; use TodoRepository; my $repo = TodoRepository->new(…); sub my_todo_list {
 my $self = shift; my $todos = $repo->select($self->user_id); return $self->render($todos); }

Slide 34

Slide 34 text

TodoApp TodoRepository DBI TODO table

Slide 35

Slide 35 text

TodoApp TodoRepository DBI TODO table

Slide 36

Slide 36 text

TodoApp TodoRepository DBI TODO table Unstable Dependencies

Slide 37

Slide 37 text

package TodoApp; use TodoRepository; my $repo = TodoRepository->new;
 sub evil_method { $repo->do(“ANY QUERY!!!!”); }

Slide 38

Slide 38 text

package TodoApp; use TodoRepository; my $repo = TodoRepository->new;
 sub evil_method { $repo->do(“ANY QUERY!!!!”); } Too many features

Slide 39

Slide 39 text

easy to test?

Slide 40

Slide 40 text

my $mock = mock TodoRepository => ( select => sub { … } ); my $app = TodoApp->new; test $app->my_todo_list;

Slide 41

Slide 41 text

my $mock = mock TodoRepository => ( select => sub { … } ); my $app = TodoApp->new; test $app->my_todo_list; messy to test

Slide 42

Slide 42 text

Problems of “Inheritance” case 1.Unstable dependencies 2.Too many features 3.messy to test

Slide 43

Slide 43 text

“Role” case

Slide 44

Slide 44 text

package TodoRepositoryInterface; use Moo::Role; requires qw(select);

Slide 45

Slide 45 text

package TodoRepository;
 use Moo; with qw( TodoRepositoryInterface ); has dbh => ( isa => ‘DBI::db’ );

Slide 46

Slide 46 text

package TodoRepository; use Moo; with qw( TodoRepositoryInterface ); has dbh => ( isa => ‘DBI::db’ );

Slide 47

Slide 47 text

package TodoRepository; … sub select { my ($self, $user_id) = @_; $self->dbh->selectall_arrayref( ‘SELECT * FROM todos WHERE user_id = ?’, $user_id ); }

Slide 48

Slide 48 text

package TodoApp; use Moo; has todo_repo => ( is => ‘ro’, does => ‘TodoRepositoryInterface’ );

Slide 49

Slide 49 text

package TodoApp; … sub my_todo_list {
 my $self = shift; my $repo = $self->todo_repo; my $todos = $repo->select($self->user_id); return $self->render($todos); }

Slide 50

Slide 50 text

TodoApp TodoRepository DBI TODO table TodoRepositoryInterface

Slide 51

Slide 51 text

TodoApp TodoRepository DBI TODO table TodoRepositoryInterface NOT!

Slide 52

Slide 52 text

TodoApp TodoRepository DBI TODO table TodoRepositoryInterface more stable

Slide 53

Slide 53 text

Problems of “Inheritance” case 1.Unstable dependencies 2.Too many features 3.messy to test

Slide 54

Slide 54 text

package TodoApp; use Moo; has todo_repo => ( is => ‘ro’, does => ‘TodoRepositoryInterface’ );
 sub evil_method { $todo_repo->do(“ANY QUERY!!!!”); }

Slide 55

Slide 55 text

package TodoApp; use Moo; has todo_repo => ( is => ‘ro’, does => ‘TodoRepositoryInterface’ );
 sub evil_method { $todo_repo->do(“ANY QUERY!!!!”); }

Slide 56

Slide 56 text

package TodoApp; use Moo; has todo_repo => ( is => ‘ro’, does => ‘TodoRepositoryInterface’ );
 sub evil_method { $todo_repo->do(“ANY QUERY!!!!”); } Necessary and sufficient features

Slide 57

Slide 57 text

Problems of “Inheritance” case 1.Unstable dependencies 2.Too many features 3.messy to test

Slide 58

Slide 58 text

easy to test?

Slide 59

Slide 59 text

package TestTodoRepository;
 use Moo; with qw( TodoRepositoryInterface ); sub select { … }

Slide 60

Slide 60 text

package TestTodoRepository;
 use Moo; with qw( TodoRepositoryInterface ); sub select { … } Be easier to test

Slide 61

Slide 61 text

Problems of “Inheritance” case 1.Unstable dependencies 2.Too many features 3.messy to test

Slide 62

Slide 62 text

no problem at all?

Slide 63

Slide 63 text

package TodoRepositoryInterface; use Moo::Role; requires qw(select); sub surprised_method { .. do anything }

Slide 64

Slide 64 text

package TodoRepositoryInterface; use Moo::Role; requires qw(select); sub surprised_method { .. do anything } Implementation reusable

Slide 65

Slide 65 text

package TodoRepositoryInterface; use Moo::Role; requires qw(select);

Slide 66

Slide 66 text

select($id) ? select(id => $id) ? select({ id => $id }) ?

Slide 67

Slide 67 text

select($id) # => arrayref of Todo ? # => iterator of Todo ?

Slide 68

Slide 68 text

select($id) # => arrayref of Todo ? # => iterator of Todo ? Ambiguous interface input / output

Slide 69

Slide 69 text

Problems of “Role” case 1.Implementation reusable 2.Ambiguous interface input / output

Slide 70

Slide 70 text

“Function::Interface” case

Slide 71

Slide 71 text

package TodoRepositoryInterface; use Function::Interface; use TodoTypes; method select(UserID $user_id) :Return(ArrayRef[Todo]);

Slide 72

Slide 72 text

Problems of “Role” case 1.Implementation reusable 2.Ambiguous interface input / output

Slide 73

Slide 73 text

package TodoRepositoryInterface; use Function::Interface; use TodoTypes; method select(UserID $user_id) :Return(ArrayRef[Todo]); sub surprising_method {
 … do anything }

Slide 74

Slide 74 text

package TodoRepositoryInterface; use Function::Interface; use TodoTypes; method select(UserID $user_id) :Return(ArrayRef[Todo]); sub surprising_method {
 … do anything }

Slide 75

Slide 75 text

Problems of “Role” case 1.Implementation reusable 2.Ambiguous interface input / output

Slide 76

Slide 76 text

package TodoRepository; use TodoTypes; use Function::Interface::Impl qw( TodoRepositoryInterface ); has dbh => ( … ); method select(UserID $user_id) :Return(ArrayRef[Todo]) { … }

Slide 77

Slide 77 text

package TodoApp; use Moo; use TodoTypes; has todo_repo => ( isa => ImplOf["TodoRepositoryInterface"], ); sub my_todo_list { … }

Slide 78

Slide 78 text

Looks Good!

Slide 79

Slide 79 text

Internal

Slide 80

Slide 80 text

requirements 1.function implementation 2.function interface 3.check if interface is implmented 4.interface type

Slide 81

Slide 81 text

1. function implementation

Slide 82

Slide 82 text

Function::Parameters • subroutine signatures • developed by MAUKE

Slide 83

Slide 83 text

use Function::Parameters; use Types::Standard -types; fun add(Int $a, Int $b) { return $a + $b }

Slide 84

Slide 84 text

my $info = Function::Parameters::info(\&add); $info->positional_required; # => Int $a, Int $b

Slide 85

Slide 85 text

Function::Return • specify function return types • created by myself

Slide 86

Slide 86 text

sub add :Return(Int) { 3.14 } add(); # ERROR! Invalid type

Slide 87

Slide 87 text

my $info = Function::Return::info(\&add); $info->types; # => Int

Slide 88

Slide 88 text

Appendix about Function::Return

Slide 89

Slide 89 text

sub multi :Return(Num, Str) { 3.14, ‘message' } my ($pi, $msg) = multi();

Slide 90

Slide 90 text

sub multi :Return(Num, Str) { 3.14, ‘message' } my ($pi, $msg) = multi(); my $count = multi(); # ERROR!

Slide 91

Slide 91 text

requirements 1.function implementation 2.function interface 3.check if interface is implemented 4.interface type

Slide 92

Slide 92 text

2. function interface

Slide 93

Slide 93 text

Function::Interface • Keyword::Simple • PPR

Slide 94

Slide 94 text

Deparse fun hello() :Return(Str);

Slide 95

Slide 95 text

sub BEGIN { Function::Interface::_register_info({ package => ‘HelloInterface', keyword => ‘fun', subname => ‘message', params => [], return => [&Str()]} ); }

Slide 96

Slide 96 text

my $info = Function::Interface::info(“HelloInterface”); $info->functions; # => hello(Str $msg) :Return(Str)

Slide 97

Slide 97 text

requirements 1.function implementation 2.function interface 3.check if interface is implemented 4.interface type

Slide 98

Slide 98 text

3.check if interface is implemented

Slide 99

Slide 99 text

use Function::Interface::Impl qw(
 FooInterface BarInterface BazInterface );

Slide 100

Slide 100 text

F::Parameters#info F::Return#info F::Interface#info

Slide 101

Slide 101 text

F::Parameters#info F::Return#info F::Interface#info

Slide 102

Slide 102 text

requirements 1.function implementation 2.function interface 3.check if interface is implemented 4.interface type

Slide 103

Slide 103 text

use Function::Interface::Types; my $type = ImplOF[‘FooInterface’]; # Foo is implements of FooInterface my $foo = Foo->new; $type->check($foo);

Slide 104

Slide 104 text

requirements 1.function implementation 2.function interface 3.check if interface is implemented 4.interface type

Slide 105

Slide 105 text

Performance

Slide 106

Slide 106 text

F::I::Impl runs at compile time

Slide 107

Slide 107 text

F::Return has `no_check` option

Slide 108

Slide 108 text

Let’s benchmark

Slide 109

Slide 109 text

F::P + F::R + F::I F::P + F::R no_check + F::I F::P + F::R no_check F::P Case1 Case4 Case2 Case3

Slide 110

Slide 110 text

F::P + F::R + F::I F::P + F::R no_check + F::I F::P + F::R no_check F::P 360653.77/s 1499189.22/s 1499189.22/s 1499189.22/s Case1 Case4 Case2 Case3

Slide 111

Slide 111 text

$BTF $BTF $BTF $BTF

Slide 112

Slide 112 text

Conclusion

Slide 113

Slide 113 text

Features of F::Interface 1.Typed interface definition 2.Check at compile time

Slide 114

Slide 114 text

Goal • To maintain large apps. • Interface is good for large apps 1. Easy to fix implementation 2. Stable Dependencies

Slide 115

Slide 115 text

Internal • function implementation • Function::Parameters + Function:\:Return • function interface • Keyword::Simple + PPR • compare meta infos at compile time

Slide 116

Slide 116 text

Performance • The same speed as using only F::Parameters • Function::Interface::Impl runs at CHECK phase • Function::Return has `no_check` option

Slide 117

Slide 117 text

Questions?

Slide 118

Slide 118 text

Thank you!