Interfaces in Perl5 at The Perl Conference 2019 in Pittsburgh

Interfaces in Perl5 at The Perl Conference 2019 in Pittsburgh

918b601dbd1297d58105508fb880b598?s=128

Kenta Kobayashi

June 17, 2019
Tweet

Transcript

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

    #TPCiP
  2. me • kobaken a.k.a @kfly8 • leader Gotanda.pm • orgnize

    YAPC::Tokyo 2019
  3. None
  4. None
  5. None
  6. None
  7. None
  8. None
  9. None
  10. None
  11. I ❤ Perl

  12. Interfaces in Perl5

  13. DBI

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

  15. Unified Interface independent of datastore

  16. Unified Interface provides values

  17. Limitation makes value

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

  19. Function::Interface is Java-like interface

  20. DEMO

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

  22. What is Goal ?

  23. To maintain large Perl apps

  24. Interface is good for large apps

  25. 1. Be easier to fix implementation

  26. 2. Stable dependencies

  27. Goal • To maintain large apps. • Interface is good

    for large apps 1. Easy to fix implementation 2. Stable Dependencies
  28. Example: TODO app

  29. Case: “Inheritance”

  30. package TodoRepository; use parent qw(DBI); sub new { my $class

    = shift; $class->connect(…); }
  31. package TodoRepository; … 
 sub select { my ($self, $user_id)

    = @_; $self->selectall_arrayref( ‘SELECT * FROM todos WHERE user_id = ?’, $user_id ); }
  32. package TodoApp; use TodoRepository; my $repo = TodoRepository->new;

  33. 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); }
  34. TodoApp TodoRepository DBI TODO table

  35. TodoApp TodoRepository DBI TODO table

  36. TodoApp TodoRepository DBI TODO table Unstable Dependencies

  37. package TodoApp; use TodoRepository; my $repo = TodoRepository->new;
 sub evil_method

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

    { $repo->do(“ANY QUERY!!!!”); } Too many features
  39. easy to test?

  40. my $mock = mock TodoRepository => ( select => sub

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

    { … } ); my $app = TodoApp->new; test $app->my_todo_list; messy to test
  42. Problems of “Inheritance” case 1.Unstable dependencies 2.Too many features 3.messy

    to test
  43. “Role” case

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

  45. package TodoRepository;
 use Moo; with qw( TodoRepositoryInterface ); has dbh

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

    => ( isa => ‘DBI::db’ );
  47. package TodoRepository; … sub select { my ($self, $user_id) =

    @_; $self->dbh->selectall_arrayref( ‘SELECT * FROM todos WHERE user_id = ?’, $user_id ); }
  48. package TodoApp; use Moo; has todo_repo => ( is =>

    ‘ro’, does => ‘TodoRepositoryInterface’ );
  49. 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); }
  50. TodoApp TodoRepository DBI TODO table TodoRepositoryInterface

  51. TodoApp TodoRepository DBI TODO table TodoRepositoryInterface NOT!

  52. TodoApp TodoRepository DBI TODO table TodoRepositoryInterface more stable

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

    to test
  54. package TodoApp; use Moo; has todo_repo => ( is =>

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

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

    ‘ro’, does => ‘TodoRepositoryInterface’ );
 sub evil_method { $todo_repo->do(“ANY QUERY!!!!”); } Necessary and sufficient features
  57. Problems of “Inheritance” case 1.Unstable dependencies 2.Too many features 3.messy

    to test
  58. easy to test?

  59. package TestTodoRepository;
 use Moo; with qw( TodoRepositoryInterface ); sub select

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

    { … } Be easier to test
  61. Problems of “Inheritance” case 1.Unstable dependencies 2.Too many features 3.messy

    to test
  62. no problem at all?

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

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

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

  66. select($id) ? select(id => $id) ? select({ id => $id

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

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

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

    output
  70. “Function::Interface” case

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

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

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

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

    sub surprising_method {
 … do anything }
  75. Problems of “Role” case 1.Implementation reusable 2.Ambiguous interface input /

    output
  76. package TodoRepository; use TodoTypes; use Function::Interface::Impl qw( TodoRepositoryInterface ); has

    dbh => ( … ); method select(UserID $user_id) :Return(ArrayRef[Todo]) { … }
  77. package TodoApp; use Moo; use TodoTypes; has todo_repo => (

    isa => ImplOf["TodoRepositoryInterface"], ); sub my_todo_list { … }
  78. Looks Good!

  79. Internal

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

    4.interface type
  81. 1. function implementation

  82. Function::Parameters • subroutine signatures • developed by MAUKE

  83. use Function::Parameters; use Types::Standard -types; fun add(Int $a, Int $b)

    { return $a + $b }
  84. my $info = Function::Parameters::info(\&add); $info->positional_required; # => Int $a, Int

    $b
  85. Function::Return • specify function return types • created by myself

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

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

  88. Appendix about Function::Return

  89. sub multi :Return(Num, Str) { 3.14, ‘message' } my ($pi,

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

    $msg) = multi(); my $count = multi(); # ERROR!
  91. requirements 1.function implementation 2.function interface 3.check if interface is implemented

    4.interface type
  92. 2. function interface

  93. Function::Interface • Keyword::Simple • PPR

  94. Deparse fun hello() :Return(Str);

  95. sub BEGIN { Function::Interface::_register_info({ package => ‘HelloInterface', keyword => ‘fun',

    subname => ‘message', params => [], return => [&Str()]} ); }
  96. my $info = Function::Interface::info(“HelloInterface”); $info->functions; # => hello(Str $msg) :Return(Str)

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

    4.interface type
  98. 3.check if interface is implemented

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

  100. F::Parameters#info F::Return#info F::Interface#info

  101. F::Parameters#info F::Return#info F::Interface#info

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

    4.interface type
  103. use Function::Interface::Types; my $type = ImplOF[‘FooInterface’]; # Foo is implements

    of FooInterface my $foo = Foo->new; $type->check($foo);
  104. requirements 1.function implementation 2.function interface 3.check if interface is implemented

    4.interface type
  105. Performance

  106. F::I::Impl runs at compile time

  107. F::Return has `no_check` option

  108. Let’s benchmark

  109. 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
  110. 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
  111.      $BTF $BTF $BTF $BTF

  112. Conclusion

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

  114. Goal • To maintain large apps. • Interface is good

    for large apps 1. Easy to fix implementation 2. Stable Dependencies
  115. Internal • function implementation • Function::Parameters + Function:\:Return • function

    interface • Keyword::Simple + PPR • compare meta infos at compile time
  116. Performance • The same speed as using only F::Parameters •

    Function::Interface::Impl runs at CHECK phase • Function::Return has `no_check` option
  117. Questions?

  118. Thank you!