Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Locked in a Box: Closures in Perl5

Locked in a Box: Closures in Perl5

Closures help improve code by providing a flexible, largely declarative way to implement re-usable code. This talk looks at the basic of closures using Perl5 and shows some ways they can provide nice solutions to common problems in any language.

Steven Lembark

July 14, 2023
Tweet

More Decks by Steven Lembark

Other Decks in Technology

Transcript

  1. 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!
  2. Closures are a basic feature of Perl5 They are just

    subs. Use lexical variables declared outside of the sub.
  3. 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.
  4. Know it when you see it message() “closes over” $verbose.

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

    meaningless. my $verbose = 1; sub message { say @_ if $verbose; } sub bar { say “Whatever”; }
  6. Know it when you see it Named subs can be

    closures. sub message { say @_ if $verbose; }
  7. Lightweight: $verbose is less overhead $ENV{ VERBOSE } my $verbose

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

    } my $verbose = $ENV{ VERBOSE }; sub debug_msg { $, = ‘ ‘; $verbose and say @_; }
  9. 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; }
  10. Applications Early: “state $start” in runtime would be initialized too

    late. my $start = time; sub runtime { my $elapsed = time - $start; say "Runtime: $elapsed sec”; }
  11. 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.
  12. 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.
  13. 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.
  14. 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.
  15. 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’;
  16. Red Flags Lots of code does validation. Repeated validaiton. get_name()

    { say ‘Enter a name:’ ... } get_path() { say ‘Enter a path:’ ... }
  17. 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:’ ... }
  18. 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:’ ... }
  19. 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’;
  20. 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”; }
  21. 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”; }
  22. 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”; }
  23. 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”; }
  24. 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;
  25. 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.
  26. 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.
  27. Wrapping objects One use: Private methods. my $small_piece = sub

    { my $obj = shift; ... }; sub big_method { ... $obj->$small_piece; ... }
  28. 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; }