Using a functional programming style on Perl 5

Christian Jaeger

LeafPair.com

 

Note: a few changes have been applied since the presentation. You can see them in the commit history at https://github.com/pflanze/london.pm-talk.

Why functional programming in Perl?

I ...

Overview

  1. Functional Programming?
  2. Can Perl do it?
  3. Combinators
  4. Linked lists, lazy evaluation
  5. Haskell style lazy sequences

Functional programming?

A functional program

Functional programming: why?

Perl can do it, Right?

Perl can do it, Right?

Perl can do it better

Perl can do it better II

Perl can do it better III

Perl can do it better III

Perl can do it better III

Perl can do it already?, cont

Perl can do it already?, cont

Perl can do it already?, cont

Perl can do it already?, cont

Perl can do it with some help

func outer ($n) {
    my $inner; $inner= func ($n, $tot) { # leaks memory!
        $n > 0 ? &$inner($n-1, $tot*$n) : $tot
    }; 
    &$inner($n, 1);
}

for (1..1e6) { outer 10 };

Perl can do it with some help

use Scalar::Util 'weaken';

sub Weakened ($) {
    my ($ref)= @_;
    weaken $_[0];
    $ref
}

func outer ($n) {
    my $inner; $inner= func ($n, $tot) {
        $n > 0 ? &$inner($n-1, $tot*$n) : $tot
    }; 
    Weakened($inner)->($n, 1);
}

for (1..1e6) { outer 10 };

... or with a trick

Fixpoint combinator

# in FP::fix
func fix ($f) {
    sub {
	tail &$f (fix($f), @_)
    }
}

func outer ($n) {
    my $inner= fix func ($inner, $n, $tot) {
        $n > 0 ? &$inner($n-1, $tot*$n) : $tot
    }; 
    &$inner($n, 1);
}

Combinators

Linked lists and lazy evaluation

Functional list generation:

repl> $l= [ 2, undef ]
$VAR1 = [ 2, undef ];
repl> $m= [ 1, $l ]
$VAR1 = [ 1, [ 2, undef ] ];
repl> $l
$VAR1 = [ 2, undef ];

repl> (cons 1, cons 2, null) -> array
$VAR1 = [ 1, 2 ];

Haskell:

Prelude> 1 : 2 : []
[1,2]

Lazy evaluation

evaluating expressions only when necessary

repl> $x = lazy { warn "evaluating!"; 2*3 }
$VAR1 = bless( ... , 'FP::Lazy::Promise' );
repl> force $x
evaluating! at (eval 104) line 1.
$VAR1 = 6;
repl> force $x
$VAR1 = 6;

repl> $x = lazy { 1 / 0 }
$VAR1 = bless( ... , 'FP::Lazy::Promise' );
repl> force $x
Illegal division by zero at (eval 112) line 1.

Haskell style


*Main> let ones = 1 : ones
*Main> take 5 ones
[1,1,1,1,1]

*Main> let alternating = True:False:alternating
*Main> take 5 alternating
[True,False,True,False,True]

Using functional-perl:

repl> func ones () { my $ones; $ones= lazy { cons 1, $ones };
                              Weakened $ones }
repl> ones->take(5)->array
$VAR1 = [ 1,1,1,1,1 ];

Haskell style: lazy sequences

Infinite stream calculated on demand:

Prelude> let fibs = 1:1:zipWith (+) fibs (tail fibs)
Prelude> take 10 fibs
[1,1,2,3,5,8,13,21,34,55]

Using functional-perl:

func fibs () {
    my $fibs; $fibs=
      cons 1, cons 1, lazy { stream_zip_with *add, $fibs, rest $fibs };
    $fibs
}
main> fibs->stream_take(10)->array
$VAR1 = [ 1,1,2,3,5,8,13,21,34,55 ];

Thanks for listening!

Questions?

Get the code from https://github.com/pflanze/functional-perl

and discuss it on functional-perl.org