Slide 1

Slide 1 text

Locked Thinking Inside the Box : Closures in Perl5 Steven Lembark Workhorse Computing [email protected]

Slide 2

Slide 2 text

Closures are a basic feature of Programming Stable values. Co-ordination between subs. Inversion of control. Wrap objects. Delay execution. Mechanism for the classe 5.38!

Slide 3

Slide 3 text

Closures are a basic feature of Perl5 They are just subs. Use lexical variables declared outside of the sub.

Slide 4

Slide 4 text

Closures are a basic feature of Perl5 They are subs. Use lexical variables declared outside of the sub. Comibined with “First Class Functions” it’s a core of functional programming.

Slide 5

Slide 5 text

Language support for First Class Functions

Slide 6

Slide 6 text

Closures are a basic feature of Perl5 Language support for First Class Functions

Slide 7

Slide 7 text

Know it when you see it message() “closes over” $verbose. my $verbose = 1; sub message { say @_ if $verbose; }

Slide 8

Slide 8 text

Know it when you see it Inside bar() $verbose is meaningless. my $verbose = 1; sub message { say @_ if $verbose; } sub bar { say “Whatever”; }

Slide 9

Slide 9 text

Know it when you see it Named subs can be closures. sub message { say @_ if $verbose; }

Slide 10

Slide 10 text

Lightweight: $verbose is less overhead $ENV{ VERBOSE } my $verbose = $ENV{ VERBOSE }; sub debug_msg { $, = ‘ ‘; $verbose and say @_; } Applications

Slide 11

Slide 11 text

Applications Stable: $verbose is immune to modifications of $ENV{ VERBOSE } my $verbose = $ENV{ VERBOSE }; sub debug_msg { $, = ‘ ‘; $verbose and say @_; }

Slide 12

Slide 12 text

Applications Co-ordinate: debug_* subs behave the same. my $verbose = $ENV{ VERBOSE }; sub debug_msg { say "Something” if $verbose; } sub debug_status { do { something } if $verbose; }

Slide 13

Slide 13 text

Applications Early: “state $start” in runtime would be initialized too late. my $start = time; sub runtime { my $elapsed = time - $start; say "Runtime: $elapsed sec”; }

Slide 14

Slide 14 text

First class functions sub generate_counter { my $value = shift; my $final = shift // 2**32-1; looks_like_number $value or die ... ; looks_like_number $final or die ... ; $value <= $final or die ... ; sub { $value > $final and return; $value++ } } anonymous subs & subref’s can be passed as arguments.

Slide 15

Slide 15 text

First class functions sub generate_counter { my $value = shift; my $final = shift // 2**32-1; looks_like_number $value or die ... ; looks_like_number $final or die ... ; $value <= $final or die ... ; sub { $value > $final and return; $value++ } } Dynamic subroutine closes over $value & $final.

Slide 16

Slide 16 text

First class functions sub generate_counter { my $value = shift; my $final = shift // 2**32-1; looks_like_number $value or die ... ; looks_like_number $final or die ... ; $value <= $final or die ... ; sub { $value > $final and return; $value++ } } Dynamic subroutine closes over $value & $final. Each instance has its own variables.

Slide 17

Slide 17 text

First class functions my $two_digit = generate_counter 10, 99 ; my $three_digit = generate_counter 100, 999 ; for(;;) { my $row = read_something; my $id = $two_digit->() // die ‘Exhausted two digit’; ... } Generate a counter as closure and use the values.

Slide 18

Slide 18 text

Red Flags Lots of code does validation. get_name() { local $SIG{ INT } = ... ; say ‘Enter a name:’ || die “Failed say.\n”; my $name = readline // die “Failed readline.\”; length $name > 6 or die “Too short: ‘$name’”; $name =~ m{ \s }x and die ‘Sorry, no whitespace’;

Slide 19

Slide 19 text

Red Flags Lots of code does validation. Repeated validaiton. get_name() { say ‘Enter a name:’ ... } get_path() { say ‘Enter a path:’ ... }

Slide 20

Slide 20 text

Red Flags Lots of code does validation. Repeated validaiton. Cut+pasted validation. get_name() { say ‘Enter a name:’ ... } get_path() { say ‘Enter a path:’ ... } get date() { say ‘Enter a date:’ ... }

Slide 21

Slide 21 text

Red Flags Lots of code does validation. Repeated validaiton. Cut+pasted validation. get_name() { say ‘Enter a name:’ ... } get_path() { say ‘Enter a path:’ ... } get date() { say ‘Enter a date:’ ... }

Slide 22

Slide 22 text

Reusable code Don’t paste, recycle! get_name() { local $SIG{ INT } = ... ; say ‘Enter a name:’ || die “Failed say.\n”; my $name = readline // die “Failed readline.\”; length $name > 6 or die “Too short: ‘$name’”; $name =~ m{ \s }x and die ‘Sorry, no whitespace’;

Slide 23

Slide 23 text

Reusable code Don’t paste, recycle! Leave the boilerplate. get_input() { local $SIG{ INT } = ... ; say || die “Failed say.\n”; my $input = readline // die “Failed readline.\”; or die “Botched input.\n”; }

Slide 24

Slide 24 text

Reusable code Don’t paste, recycle! Pass in the specifics. get_input() { my ( $prompt, $sanity ) = @_; local $SIG{ INT } = ... ; say $prompt || die “Failed say.\n”; my $input = readline // die “Failed readline.\”; $input->$sanity or die “Botched input.\n”; }

Slide 25

Slide 25 text

Reusable code Don’t paste, recycle! Inversion of Control get_input() { my ( $prompt, $sanity ) = @_; local $SIG{ INT } = ... ; say $prompt || die “Failed say.\n”; my $input = readline // die “Failed readline.\”; $input->$sanity or die “Botched input.\n”; }

Slide 26

Slide 26 text

Sanity checks date_sanity generates sanity checks. $cutoff can come from anywhere. my $sanity = date_sanity $cutoff;

Slide 27

Slide 27 text

Sanity checks date_sanity generates sanity checks. Short, specific, declarative. my $sanity = date_sanity $cutoff; sub date_sanity { my $first = shift; sub { my $val = shift; my $time = str2time $val or die “Bogus date $val\n”; $time < $first or die “$val < $first\n”; }

Slide 28

Slide 28 text

Simpler code get_input now fully reusable. my $date_check = date_sanity ... ; my $name_check = name_sanity ... ; my $path_check = path_sanity ... ; my $date = get_input Date => $date_sanity; my $name = get_input Name => $name_sanity; my $path = get_input Path => $path_sanity;

Slide 29

Slide 29 text

Nesting Closures sub generate_handler { my ( $prompt, $sanity ) = @_; my $continue = ‘’; my $handler = sub { $continue = ‘’ }; sub { $continue = 1; local $SIG{ INT } = $handler ; for( my $val ; $continue ; ) { $val = eval { say $prompt // die ... my $input = readline or die ... $input->$sanity or die ... } // next; return input; } } } Caller passes prompt and sanity check. Gets back an input handler as a closure.

Slide 30

Slide 30 text

Nesting Closures my $get_date = gen_handler Date => $date_sanity; while( my $date = $get_date->() ) ( process_date $date; ) Caller passes prompt and sanity check. Gets back an input handler. Zero cut+paste.

Slide 31

Slide 31 text

Wrapping objects There Perl’s dispatch: $obj->foobar Determined at runtime.

Slide 32

Slide 32 text

Wrapping objects There Perl’s dynamic dispatch: $obj->$name Determined at runtime.

Slide 33

Slide 33 text

Wrapping objects There Perl’s dynamic dispatch: $obj->$subref Bypass package lookup.

Slide 34

Slide 34 text

Wrapping objects One use: Private methods. my $small_piece = sub { my $obj = shift; ... }; sub big_method { ... $obj->$small_piece; ... }

Slide 35

Slide 35 text

Wrapping objects Use a closure: Public method calls private handler. my $value = read_config; my $handler = gen_method $value; sub generic { my $obj = shift; ... $obj->$handler; }

Slide 36

Slide 36 text

Beside Reading First Class Functions: https://en.wikipedia.org/wiki/First-class_function Thanks to Damian Conway for the use of his Functional Programming in Perl & Raku class notes.