103
Functional pe(a)rls London Perl Workshop 29/11/2008 version 2 osfameron

Functional Pe(a)rls version 2

Embed Size (px)

DESCRIPTION

Some techniques from the heady world of Functional Programming implemented in idiomatic Perl using various techniques: closures, iterators, Devel::Declare, and some distilled evil. New version now with monads! (As presented at http://conferences.yapceurope.org/lpw2008/ )

Citation preview

Page 1: Functional Pe(a)rls version 2

Functional pe(a)rls

London Perl Workshop29/11/2008

version 2osfameron

Page 2: Functional Pe(a)rls version 2

A sequence

Debolaz, on #moose a list of numbers like

10, 25, 50,100, 250, 500,1000, etc

without tracking any other state than the number itself

Page 3: Functional Pe(a)rls version 2

A sequence

1. (power of 10) * 2.5

2. * 23. * 2

Page 4: Functional Pe(a)rls version 2

A sequence

without tracking any other state

than the number itself

Page 5: Functional Pe(a)rls version 2

A sequence

/^1/

Page 6: Functional Pe(a)rls version 2

A sequence

/^1/ # power of 10

Page 7: Functional Pe(a)rls version 2

A sequence

cycle [2.5, 2, 2]

Page 8: Functional Pe(a)rls version 2

A sequence

cycle [2.5, 2, 2] 2.5 2 2 2.5 ...

Page 9: Functional Pe(a)rls version 2

A sequence

Oops! Perl doesn't have infinite sequences

Page 10: Functional Pe(a)rls version 2

A sequence

Iterators

Page 11: Functional Pe(a)rls version 2

A sequence

curry cycle (@list) { my @curr = @list; return sub { @curr = @list unless @curr; return shift @curr; };}

Page 12: Functional Pe(a)rls version 2

A sequence

my $c = cycle [2.5, 2, 2];

Page 13: Functional Pe(a)rls version 2

A sequence

my $c = cycle [2.5, 2, 2]; say $c->(); # 2.5 say $c->(); # 2 say $c->(); # 2 say $c->(); # 2.5 ...

Page 14: Functional Pe(a)rls version 2

A sequence

cycle [2.5, 2, 2];

Page 15: Functional Pe(a)rls version 2

A sequence

10, cycle [2.5, 2, 2];

Page 16: Functional Pe(a)rls version 2

A sequence

mult, 10, cycle [2.5, 2, 2];

Page 17: Functional Pe(a)rls version 2

(curry)

curry mult ($x,$y) { $x * $y }

Page 18: Functional Pe(a)rls version 2

(curry)

say mult( 3, 4 );

# 12

Page 19: Functional Pe(a)rls version 2

(curry)

my $triple = mult(3);say $triple->(10);

# 30

Page 20: Functional Pe(a)rls version 2

(curry)

my $multiply = mult;say $multiply->(6, 8);

Page 21: Functional Pe(a)rls version 2

(curry)

my $multiply = \&mult;my $multiply = mult;

Page 22: Functional Pe(a)rls version 2

A sequence

mult, 10, cycle [2.5, 2, 2];

Page 23: Functional Pe(a)rls version 2

A sequence

scan

mult, 10, cycle [2.5, 2, 2];

Page 24: Functional Pe(a)rls version 2

(Scanning)

scan add, 1, [2, 3, 4];

# [1, 3, 6, 10]

Page 25: Functional Pe(a)rls version 2

A sequence

scan mult,

10, cycle [2.5, 2, 2];

Page 26: Functional Pe(a)rls version 2

A sequence

take 12 => scan

mult, 10, cycle [2.5, 2, 2];

Page 27: Functional Pe(a)rls version 2

A sequence

say for take 12 => scan

mult, 10, cycle [2.5, 2, 2];

Page 28: Functional Pe(a)rls version 2

A sequence

102550100250500 ...

Page 29: Functional Pe(a)rls version 2

How?

Iterators (HOP)HOP::StreamLazy variables

Page 30: Functional Pe(a)rls version 2

How?

Iterators (HOP)FP (scan, take)

Page 31: Functional Pe(a)rls version 2

How?

Iterators (HOP)FP (scan, take)Sub::Curried

Page 32: Functional Pe(a)rls version 2

How?

Iterators (HOP)FP (scan, take)Sub::CurriedDevel::Declare

Page 33: Functional Pe(a)rls version 2

Devel::Declare

curry mult ($x,$y) { $x * $y }

Page 34: Functional Pe(a)rls version 2

Devel::Declare

New syntax! (But better than source filters)

Page 35: Functional Pe(a)rls version 2

Devel::Declare

New syntax! (But better than source filters)

Method declaration MooseX::Declare Sub::Auto

Page 36: Functional Pe(a)rls version 2

Devel::Declare

New syntax! (But better than source filters)

Currying Monads Pattern Matching List comprehensions Functions that work on @arrays and lists

Page 37: Functional Pe(a)rls version 2

Devel::Declare

In Perl! (With a bit of scary XS magic)

hooks into the compiler changing the source as you compile horrible perl tricks to get methods installed, and

braces closed mst++, rafl++

Page 38: Functional Pe(a)rls version 2

Some light relief

Page 39: Functional Pe(a)rls version 2

Some light relief

Monads

Page 40: Functional Pe(a)rls version 2

Monads

Come from maths “Category theory”

Page 41: Functional Pe(a)rls version 2

Monads

Come from maths “Category theory” Very clever people rave about them being

useful

Page 42: Functional Pe(a)rls version 2

Monads

Come from maths “Category theory” Very clever people rave about them being

useful Have a reputation for being hard to understand

Page 43: Functional Pe(a)rls version 2

Monad tutorials

Page 44: Functional Pe(a)rls version 2

Monad tutorials

Page 45: Functional Pe(a)rls version 2

Monad tutorials

Page 46: Functional Pe(a)rls version 2

Monad tutorials

Page 47: Functional Pe(a)rls version 2

Monad tutorials

Step 1: Write Monad Tutorial

Page 48: Functional Pe(a)rls version 2

Monad tutorials

Step 2: ???

Page 49: Functional Pe(a)rls version 2

Monad tutorials

Step 3: Profit!

Page 50: Functional Pe(a)rls version 2

Monads

You already use monads

Page 51: Functional Pe(a)rls version 2

Monads

You already use monads

YAY!

Page 52: Functional Pe(a)rls version 2

Monads

You already use monads Sequences of commands?

Page 53: Functional Pe(a)rls version 2

Sequencing

my $x = 1;my $y = 2;my $z = $x * $y;say “$x * $y = $z”;

Page 54: Functional Pe(a)rls version 2

Sequencing

my $x = 1;

my $y = 2;

my $z = $x * $y;

say “$x * $y = $z”;

Page 55: Functional Pe(a)rls version 2

Sequencing

my $x = 1;

my $y = 2;

my $z = $x * $y;

say “$x * $y = $z”;

Page 56: Functional Pe(a)rls version 2

Sequencing

my @seq = sub { my $x = 1 }, sub { my $y = 2 }, sub { my $z = $x * $y }, sub { say "$x * $y = $z" };

Page 57: Functional Pe(a)rls version 2

Sequencing

my @seq = sub { my $x = 1 }, sub { my $y = 2 }, sub { my $z = $x * $y }, sub { say "$x * $y = $z" };

# Global symbol "$x" requires explicit package name at ...

Page 58: Functional Pe(a)rls version 2

Nesting

my $x = 1;

my $y = 2;

my $z = $x * $y;

say “$x * $y = $z”;

Page 59: Functional Pe(a)rls version 2

Nesting

sub { my $x = 1; sub { my $y = 2; sub { my $z = $x * $y; sub { say "$x * $y = $z"; }->() }->() }->() }->();

Page 60: Functional Pe(a)rls version 2

Monads made pretty

Source filters! http://sleepingsquirrel.org/monads/monads.html

Page 61: Functional Pe(a)rls version 2

Monads made pretty

Source filters! http://sleepingsquirrel.org/monads/monads.html

Source tree manipulation (B::OP magic) Deparse and source text munging

Page 62: Functional Pe(a)rls version 2

Monads made pretty

We want a syntax like mdo { my $x = mbind(1); my $y = mbind(2); my $z = mbind($x + $y); say “$x * $y = $z”;}

Page 63: Functional Pe(a)rls version 2

Monads made pretty

We want a syntax like mdo { my $x = mbind(1); my $y = mbind(2); my $z = mbind($x + $y); say “$x * $y = $z”;}

mdo introduces the block mbind gives us a hook to rotate around

Page 64: Functional Pe(a)rls version 2

Optree munging 19: my $x << Just 2; ...

n <;> nextstate(main 2078 b.pl:19)v:*,&,$ ->ot <2> left_shift[t3] vK ->uo <0> padsv[$x:2078,2080]sM/LVINTRO ->ps <1> entersub[t2] sKS/TARG,3 ->t- <1> ex-list sK ->sp <0> pushmark s ->qq <$> const(IV 2) sM ->r- <1> ex-rv2cv sK/130 ->-r <$> gv(*Just) s ->su <;> nextstate(main 2079 b.pl:20)v:*,&,$ ->v

# : mbind (Just 2), sub { my $x = shift; ... }; <;> nextstate(main b.pl:) v:*,&,{,$ -> <@> list K -> <0> pushmark s -> <1> entersub[t2] KS/TARG,3 ->- <1> ex-list K -> <0> pushmark s -> <1> entersub[t1] lKMS/NO(),TARG,INARGS,3 ->- <1> ex-list lK -> <0> pushmark s -> <$> const(IV 2) sM ->- <1> ex-rv2cv sK/130 ->- <$> gv(*Just) s ->- <1> ex-rv2cv sK/2 ->- # mbind instead of >> <$> gv(*mbind) s -> <1> refgen K/1 ->- <1> ex-list lKRM -> <0> pushmark sRM -> <$> anoncode[CV ] lRM -> # ??? set up anon sub # my $x = shift <0> padsv[$x:2078,2080] sM/LVINTRO ->p # the next ; is moved into this new lambda! <;> nextstate(main 2079 b.pl:20) v:*,&,$ ->v

Page 65: Functional Pe(a)rls version 2

Optree munging 19: my $x << Just 2; ...

n <;> nextstate(main 2078 b.pl:19)v:*,&,$ ->ot <2> left_shift[t3] vK ->uo <0> padsv[$x:2078,2080]sM/LVINTRO ->ps <1> entersub[t2] sKS/TARG,3 ->t- <1> ex-list sK ->sp <0> pushmark s ->qq <$> const(IV 2) sM ->r- <1> ex-rv2cv sK/130 ->-r <$> gv(*Just) s ->su <;> nextstate(main 2079 b.pl:20)v:*,&,$ ->v

# : mbind (Just 2), sub { my $x = shift; ... }; <;> nextstate(main b.pl:) v:*,&,{,$ -> <@> list K -> <0> pushmark s -> <1> entersub[t2] KS/TARG,3 ->- <1> ex-list K -> <0> pushmark s -> <1> entersub[t1] lKMS/NO(),TARG,INARGS,3 ->- <1> ex-list lK -> <0> pushmark s -> <$> const(IV 2) sM ->- <1> ex-rv2cv sK/130 ->- <$> gv(*Just) s ->- <1> ex-rv2cv sK/2 ->- # mbind instead of >> <$> gv(*mbind) s -> <1> refgen K/1 ->- <1> ex-list lKRM -> <0> pushmark sRM -> <$> anoncode[CV ] lRM -> # ??? set up anon sub # my $x = shift <0> padsv[$x:2078,2080] sM/LVINTRO ->p # the next ; is moved into this new lambda! <;> nextstate(main 2079 b.pl:20) v:*,&,$ ->v

KABOOM

Page 66: Functional Pe(a)rls version 2

Source deparsing

Works surprisingly well

Page 67: Functional Pe(a)rls version 2

Source deparsing

Works surprisingly well for trivial cases (a bit fragile)

Page 68: Functional Pe(a)rls version 2

Source deparsing

Works surprisingly well for trivial cases (a bit fragile) though localised to mdo { ... }

Page 69: Functional Pe(a)rls version 2

Devel::Declare

Even nicer syntax mdo { mbind $x = 1; mbind $y = 2; mbind $z = $x + $y; say “$x * $y = $z”;}

Page 70: Functional Pe(a)rls version 2

Devel::Declare

And cuter implementation: mdo { mbind $x = 1; mbind $y = 2; mbind $z = $x + $y; say “$x * $y = $z”;

}

Page 71: Functional Pe(a)rls version 2

Devel::Declare

The problem: mdo { mbind 1, sub { my $x = shift; mbind 2, sub { my $y = shift; mbind $x + $y, sub { my $z = shift; say “$x * $y = $z”;

} ...

Page 72: Functional Pe(a)rls version 2

Devel::Declare

No need to count nesting: scope_inject Scope::Guard Use to inject a ; at the end of method

declarations: method foo ($x) { print $x;} # look Ma, no semicolon!

Page 73: Functional Pe(a)rls version 2

Devel::Declare

mbind's scope_inject adds a “}” mdo { mbind 1, sub { my $x = shift; mbind 2, sub { my $y = shift; mbind $x + $y, sub { my $z = shift; say “$x * $y = $z”;

} # adds a closing brace

}

Page 74: Functional Pe(a)rls version 2

Devel::Declare

mbind's scope_inject adds a “}” mdo { mbind 1, sub { my $x = shift; mbind 2, sub { my $y = shift; mbind $x + $y, sub { my $z = shift; say “$x * $y = $z”; }

} # adds a closing brace

}

Page 75: Functional Pe(a)rls version 2

Devel::Declare

mbind's scope_inject adds a “}” mdo { mbind 1, sub { my $x = shift; mbind 2, sub { my $y = shift; mbind $x + $y, sub { my $z = shift; say “$x * $y = $z”; } }

} # adds a closing brace

}

Page 76: Functional Pe(a)rls version 2

Devel::Declare

mbind's scope_inject adds a “}” mdo { mbind 1, sub { my $x = shift; mbind 2, sub { my $y = shift; mbind $x + $y, sub { my $z = shift; say “$x * $y = $z”; } } } } # closes block

Page 77: Functional Pe(a)rls version 2

So...

We can now sequence commands! mdo { mbind $x = 1; mbind $y = 2; mbind $z = $x + $y; say “$x * $y = $z”;}

Page 78: Functional Pe(a)rls version 2

So...

We can now sequence commands!

YAY?

Page 79: Functional Pe(a)rls version 2

So...

We can now sequence commands! OK, so this was big news in Haskell in 1990s

Page 80: Functional Pe(a)rls version 2

So...

We can now sequence commands! OK, so this was big news in Haskell in 1990s Imperative languages have always done this

Page 81: Functional Pe(a)rls version 2

What else can monads do ?

Sequencing mdo { mbind $x = 1; mbind $y = 2; mbind $z = $x + $y; say “$x * $y = $z”;}

Page 82: Functional Pe(a)rls version 2

What else can monads do ?

Sequencing mdo { mbind $x = 1; mbind $y = 2; mbind $z = $x + $y; say “$x * $y = $z”;}

Page 83: Functional Pe(a)rls version 2

What else can monads do ?

Sequencing mdo { mbind $x = 1; mbind $y = 2; mbind $z = $x + $y; say “$x * $y = $z”;}

Page 84: Functional Pe(a)rls version 2

What else can monads do ?

Sequencing mdo { mbind $x = 1; mbind $y = 2; mbind $z = $x + $y; say “$x * $y = $z”;}

Programmable semicolon!

Page 85: Functional Pe(a)rls version 2

Maybe

Success/Failure mdo (Maybe) { mbind $FH = m_open('<', $file); mbind $line = <$FH>; mbind $val = lookup(\%h, $line); say “Found $val!”;}

Page 86: Functional Pe(a)rls version 2

Maybe

Success/Failure mdo (Maybe) { mbind $FH = m_open('<', $file); mbind $line = <$FH>; mbind $val = lookup(\%h, $line); say “Found $val!”;}

Will give up if can't open file, read a line from it, or look it up in a hash

Page 87: Functional Pe(a)rls version 2

Maybe

Success/Failure mdo (Maybe) { mbind $FH = m_open('<', $file); mbind $line = <$FH>; mbind $val = lookup(\%h, $line); say “Found $val!”;}

Compare chain of if (foo) { if (bar) { ... or eval { ... }

Page 88: Functional Pe(a)rls version 2

Maybe

$FH = open($file)

$line = <$FH>

$val = lookup($line)

say “Found $val”

Page 89: Functional Pe(a)rls version 2

Maybe

$FH = open($file)

$line = <$FH>

$val = lookup($line)

say “Found $val”

Page 90: Functional Pe(a)rls version 2

Sequence

Page 91: Functional Pe(a)rls version 2

Maybe

??

?

Page 92: Functional Pe(a)rls version 2

Multiple (List)

Page 93: Functional Pe(a)rls version 2

List

$x = [1..10]

$y = [1..10]

guard $x+$y == 10

say “$x+$y=10”

Page 94: Functional Pe(a)rls version 2

List

Cartesian product mdo { mbind $x = [1..10]; mbind $y = [1..10]; mbind guard $x+$y == 10; say “$x+$y=10”;}

Run every $x against every $y and filter

Page 95: Functional Pe(a)rls version 2

List

Cartesian product mdo { mbind $x = [1..10]; mbind $y = [1..10]; mbind guard $x+$y == 10; say “$x+$y=10”;}

1+9=10 2+8=10 ...

Page 96: Functional Pe(a)rls version 2

List

Cartesian product mdo { mbind $x = [1..10]; mbind $y = [1..10]; mbind guard $x+$y == 10; say “$x+$y=10”;}

just like SQL or LINQ

Page 97: Functional Pe(a)rls version 2

List comprehension

More compact syntax mcomp ($x <- [1..10]; $y <- [1..10]; $x+y==10) { say “$x+$y=10” }

Page 98: Functional Pe(a)rls version 2

List

mdo { mbind $x = [1..10]; mbind $y = [1..10]; mbind guard $x+$y == 10; say “$x+$y=10”;}

We're actually calling mbind on a list

Page 99: Functional Pe(a)rls version 2

List

mdo { mbind $x = [1..10]; mbind $y = [1..10]; mbind guard $x+$y == 10; say “$x+$y=10”;}

We're actually calling mbind on a list

autobox

Page 100: Functional Pe(a)rls version 2

Perl++

Not really a functional language... But you can take it surprisingly far (CPAN++)

Page 101: Functional Pe(a)rls version 2

What else can I do with monads?

Parsing Error handling SQL generation Continuations (real resumable ones)

Page 102: Functional Pe(a)rls version 2

Wants monads now!

Techniques are valuable now Nicely wrapped implementation will be ready

soon....

Page 103: Functional Pe(a)rls version 2

Thanks!

questions?