Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Taking Perl to Eleven with Higher-Order Functions

61 views

Published on

Sometimes, you just need your Perl to go one higher. This talk will teach you how to use functions that return functions for powerful, succinct solutions to some repetitive coding problems. Along the way, you’ll see concrete examples using higher-order Perl to generate declarative, structured “fake” data for testing.

Published in: Software
  • Be the first to comment

  • Be the first to like this

Taking Perl to Eleven with Higher-Order Functions

  1. 1. Taking Perl to Eleven David Golden • @xdg Staff Engineer, MongoDB 
 The Perl Conference 2018
  2. 2. “If we need that extra push…
 do you know what we do?” Licensed under Fair use via Wikipedia - https://en.wikipedia.org/wiki/File:Spinal_Tap_-_Up_to_Eleven.jpg
  3. 3. Licensed under Fair use via Plover - http://hop.perl.plover.com/cover-full.jpg
  4. 4. Functions creating functions
  5. 5. Higher-order programming is
 going to eleven
  6. 6. Building blocks
  7. 7. Building block 1:
 anonymous functions
  8. 8. my $fcn = sub { ... }; my $str = $fcn->();
  9. 9. my $fcn = sub { return join(" ", @_) }; my $str = $fcn->(@words);
  10. 10. Building block 2:
 scope
  11. 11. my $separator = " "; sub joiner { return join($separator, @_) }
  12. 12. Building block 3:
 closure
  13. 13. my $separator = " "; sub joiner { return join($separator, @_); } }
  14. 14. sub joiner_maker { my $separator = shift; return sub { return join($separator, @_); } };
  15. 15. What happens to
 $separator?
  16. 16. Anonymous function 'closes over'
 the lexical variable
  17. 17. sub joiner_maker { my $separator = shift; return sub { return join($separator, @_); } };
  18. 18. my @words = qw/able baker/; my $spacer = joiner_maker(" "); my $dasher = joiner_maker("-"); my $banger = joiner_maker("!"); $spacer->(@words); # "able baker" $dasher->(@words); # "able-baker" $banger->(@words); # "able!baker"
  19. 19. HOP
  20. 20. Functions creating functions
  21. 21. Abstract rules for generating arbitrary data
  22. 22. Why do I want that?
  23. 23. Load testing?
  24. 24. Similar, but
 slightly different
 things Lots of
  25. 25. Start with simple
 generators...
  26. 26. Generate an
 array of integers
  27. 27. # 100 random ints from 0 to 255 sub make_int_array { my @array; push @array, int(rand(256)) for 1..100; return @array; }
  28. 28. Ick! Constants!
  29. 29. # 100 random ints from 0 to 255 sub make_int_array { my @array; push @array, int(rand(256)) for 1..100; return @array; }
  30. 30. # N random ints from 0 to X sub make_int_array { my ($n, $x) = @_; my @array; push @array, int(rand($x+1)) for 1..$n; return @array; }
  31. 31. Generate an
 array of discrete choices
  32. 32. # N strings drawn from discrete set sub make_discrete_array { my ($n, @list) = @_; my $s = @list; my @array; push @array, $list[int(rand($s))] for 1..$n; return @array; }
  33. 33. my $ref = make_discrete_array( 100, 'Larry Wall', 'Damian Conway', 'Ricardo Signes', 'Sawyer X', ... );
  34. 34. Repeat for every kind
 of array you need?!?
  35. 35. NO!
  36. 36. Generalize array generation
  37. 37. # N elements provided by a function sub make_array { my ($n, $f) = @_; my @array; push @array, $f->() for 1..$n; return @array; }
  38. 38. # N elements provided by a function sub make_array { my ($n, $f) = @_; return [ map { $f->() } 1..$n ]; }
  39. 39. # 100 random ints from 0 to 255 make_array( 100, sub { int(rand(256) } );
  40. 40. # 100 discrete choices my @list = qw(...); my $s = @list; make_array( 100, sub { $list[int(rand($s))] } );
  41. 41. Ick! Constants! (And sub {...})
  42. 42. Let's go one higher! Licensed under Fair use via Wikipedia - https://en.wikipedia.org/wiki/File:Spinal_Tap_-_Up_to_Eleven.jpg
  43. 43. Generalize the value generating functions
  44. 44. sub int_maker { my ( $min, $max ) = @_; my $range = $max - $min + 1; return sub { $min + int(rand($range)); }; } # random integer from 3 to 6 $f = int_maker(3,6) $i = $f->(); # e.g. 4
  45. 45. sub pick_maker { my (@list) = @_; my $s = @list; return sub { $list[ int(rand($s)) ] }; } # random pick from a list of names $f = pick_maker(@names) $n = $f->(); # e.g. "Larry"
  46. 46. Higher-order value generation
  47. 47. Generators replace anonymous functions
  48. 48. # 100 random ints from 0 to 255 make_array( 100, sub { int(rand(256) } ); # 100 discrete choices my @list = qw(...); my $s = @list; make_array( 100, sub { $list[int(rand($s))] } );
  49. 49. # 100 random ints from 0 to 255 make_array( 100, int_maker(0,255) ); # 100 discrete choices my @list = qw(...); make_array( 100, pick_maker(@list) );
  50. 50. Simpler & Expressive
  51. 51. # 100 random ints from 0 to 255 make_array( 100, sub { int(rand(256) } ); # 100 discrete choices my @list = qw(...); my $s = @list; make_array( 100, sub { $list[int(rand($s))] } );
  52. 52. # 100 random ints from 0 to 255 make_array( 100, int_maker(0,255) ); # 100 discrete choices my @list = qw(...); make_array( 100, pick_maker(@list) );
  53. 53. We have higher-order
 value generators
  54. 54. Why stop there?
  55. 55. Higher order
 array generation!
  56. 56. sub make_array { my ($n, $f) = @_; return [ map { $f->() } 1..$n ]; }
  57. 57. sub make_array { my ($n, $f) = @_; return [ map { $f->() } 1..$n ]; }
  58. 58. sub array_maker { my ($n, $f) = @_; return sub { return [ map { $f->() } 1..$n ]; }; }
  59. 59. # old way $r = make_array( 100, int_maker(0,255) );
  60. 60. # old way $r = make_array( 100, int_maker(0,255) ); # higher-order way $f = array_maker( 100, int_maker(0,255) ); $r = $f->();
  61. 61. Which lets us do this…
  62. 62. # array of array of integers $f = array_maker( 100, array_maker( 100, int_maker(0,255) ) ); $r = $f->();
  63. 63. Or even this...
  64. 64. # AoAoA: 100x100 RGB values? $f = array_maker( 100, array_maker( 100, array_maker( 3, int_maker(0,255) ) ) );
  65. 65. Arrays of random size?
  66. 66. Generalize $n?
  67. 67. # array of array of integers $f = array_maker( sub { 100 }, array_maker( sub { 100 }, int_maker(0,255) ) ); $r = $f->();
  68. 68. Ick!
  69. 69. # transformation helper function sub _x { my $v = shift; my $is_code = ref($v) eq 'CODE'; return $is_code ? $v->() : $v; }
  70. 70. sub array_maker { my ($n, $f) = @_; return sub { return [ map { _x($f) } 1.._x($n) ]; }; }
  71. 71. One function Many outcomes
  72. 72. # constant size, constant value $f = array_maker( $n, $v ); $r = $f->();
  73. 73. # constant size, random values $f = array_maker( $n, pick_maker(@options) ); 
 $r = $f->();
  74. 74. # random size, random values $f = array_maker( int_maker(3,6), pick_maker(@options) ); $r = $f->();
  75. 75. # nested random AoA $f = array_maker( int_maker(3,6), array_maker( int_maker(10,20), pick_maker(@options) ) ); $r = $f->();
  76. 76. Data::Fake
  77. 77. Goal: create lots of fake super-hero records
  78. 78. Licensed under Fair use via Wikipedia - https://en.wikipedia.org/wiki/File:TICK_COMIC_CON_EXTRAVAGANZA_1.jpg
  79. 79. { name => 'The Tick', battlecry => 'Spoon!', birthday => '1973-07-16', friends => [ 'Arthur', 'Die Fledermaus', 'American Maid', ], }
  80. 80. Data::Fake
 Higher-order structured data
  81. 81. use Data::Fake qw/Core Names Text Dates/; my $hero_factory = fake_hash( { name => fake_name(), battlecry => fake_sentences(1), birthday => fake_past_datetime("%Y-%m-%d"), friends => fake_array(fake_int(2,4),fake_name()), } );
  82. 82. Declarative
  83. 83. { name => 'The Tick', battlecry => 'Spoon!', birthday => '1973-07-16', friends => [ 'Arthur', 'Die Fledermaus', 'American Maid', ], } { name => fake_name(), battlecry => fake_sentences(1), birthday => fake_past_datetime("%Y-%m-%d"), friends => fake_array(fake_int(2,4),fake_name()), }
  84. 84. use Data::Fake qw/Core Names Text Dates/; my $hero_factory = fake_hash( { name => fake_name(), battlecry => fake_sentences(1), birthday => fake_past_datetime("%Y-%m-%d"), friends => fake_array(fake_int(2,4),fake_name()), } );
  85. 85. use Data::Fake qw/Core Names Text Dates/; my $hero_factory = fake_hash( { name => fake_name(), battlecry => fake_sentences(1), birthday => fake_past_datetime("%Y-%m-%d"), friends => fake_array(fake_int(2,4),fake_name()), } );
  86. 86. use Data::Fake qw/Core Names Text Dates/; my $hero_factory = fake_hash( { name => fake_name(), battlecry => fake_sentences(1), birthday => fake_past_datetime("%Y-%m-%d"), friends => fake_array(fake_int(2,4),fake_name()), } );
  87. 87. use Data::Fake qw/Core Names Text Dates/; my $hero_factory = fake_hash( { name => fake_name(), battlecry => fake_sentences(1), birthday => fake_past_datetime("%Y-%m-%d"), friends => fake_array(fake_int(2,4),fake_name()), } );
  88. 88. use Data::Fake qw/Core Names Text Dates/; my $hero_factory = fake_hash( { name => fake_name(), battlecry => fake_sentences(1), birthday => fake_past_datetime("%Y-%m-%d"), friends => fake_array(fake_int(2,4),fake_name()), } );
  89. 89. Sample output
  90. 90. # $hero_factory->() { 'name' => 'Angel Deon Rush', 'battlecry' => 'Et ratione ipsam provident.' 'birthday' => '1997-04-17', 'friends' => [ 'Josie Vivian Greer', 'Kaylee Nyla Thompson', 'Eliana Alejandra Velazquez', 'Jamie Ryan Bond' ], }
  91. 91. # $hero_factory->() { 'name' => 'Wade Stetson Johnson' 'battlecry' => 'Omnis exercitationem inventore ab.', 'birthday' => '1983-02-20', 'friends' => [ 'Carter Alaysia Bryan', 'Christina Milan Hyde', 'Ivanna Kyla Fox', 'Jocelynn Aisha Fuentes' ], }
  92. 92. # $hero_factory->() { 'name' => 'Alison Ayla Monroe' 'battlecry' => 'Aliquid et vel fuga.', 'birthday' => '1996-02-21', 'friends' => [ 'Amalia Hailey Johnston', 'Ashton Messiah Frederick', 'Aleena Genesis Holloway' ], }
  93. 93. Similar, but
 slightly different
 things Lots of
  94. 94. Recap
  95. 95. • Closures → anonymous functions with state • HOP → functions generating functions • Array generation → generalized behaviors • Data::Fake → declarative structured data
  96. 96. Take your Perl to eleven! Licensed under Fair use via Wikipedia - https://en.wikipedia.org/wiki/File:Spinal_Tap_-_Up_to_Eleven.jpg

×