Functional Pearls 4 (YAPC::EU::2009 remix)

download Functional Pearls 4 (YAPC::EU::2009 remix)

If you can't read please download the document

Transcript of Functional Pearls 4 (YAPC::EU::2009 remix)

osfameron

Functional Pe(a)rls

Functional pe(a)rls World tour!

IPW 2008LPW 2008NWE.pm May 2009YAPC::EU Lisbon 2009

version 4Osfameron

Functional Programming

What is it?

Functional Programming

About functions?

Functional Programming

About
functions?(like Object-oriented is about objects?)

Functional Programming

everything's a function!

Functional Programming

1 + 2

Functional Programming

1 + 2

Spot the
function?

Functional Programming

1 + 2

Spot the
function?

Functional Programming

1 + 2

first class?

Functional Programming

1 + 2

\+

Functional Programming

1 + 2

op

OPERATORS UNITE!

STOP this discrimation now!

What now?

Protest!

Cry!

Steal from Haskell!

What now?

Steal from Haskell!

(+) ref to add

Wrapping operators

sub op ($op) {
return
sub ($left, $right) {
eval $left $op $right;
};
}

Wrapping operators

my $add = op('+');

$add->(1, 2); # 3

YAY!

YAY?

Still uglier than Haskell (+)

op('+')

Devel::Declare

New syntax!

(But better than source filters)

Devel::Declare

New syntax!

(But better than source filters)Method declaration

MooseX::Declare

Sub::Auto

Sub::Section

on github

Gives nice syntax, using Devel::Declare

op(+)

Sub::Section

on github

Gives nice syntax, using Devel::Declare

op (Bwahaha!) (+)

Devel::Declare custom parser hook can inject code etc.

Sub::Section

on github

Gives nice syntax, using Devel::Declare

op (Bwahaha!) ('+')

Sub::Section

on github

Gives nice syntax, using Devel::Declare

Op('+') # Perl is none the wiser

Tee hee!

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++

Sections

Sections

(+) isn't really a section

Sections

(+) isn't really a section

(+1) is

Sections

(+) 10, 5 # 15

(+1) 8 # 9

Currying

Partial application

Currying

Partial application

1 arg at a time

Currying

sub add ($left, $right) {
return $left + $right;
}

Currying

sub add ($left, $right) {
return $left + $right;
}

(That's 2 arguments)

Curried functions

sub add ($left, $right) {
return $left + $right;
}

(That's 2 arguments)

add(5) ...

Curried functions

sub add ($left, $right) {
return $left + $right;
}

(That's 2 arguments)

add(5) # $left is bound to 5

Curried functions

sub add ($left, $right) {
return $left + $right;
}

(That's 2 arguments)

add(5) # $left is bound to 5
->(6); # 11

Implement in Perl

Quite simple to do with closures:

sub add {
my $left = shift;
return sub {
my $right = shift;
return $left + $right;
}
}

Implement in Perl

Quite simple to do with closures:

sub add {
my $left = shift;
return sub {
my $right = shift;
return $left + $right;
}
}

Not pretty or convenient though

Sub::Curried

on CPAN

Gives nice syntax, using Devel::Declare

sub add ($left, $right) {
return $left + $right;
}

Sub::Curried

on CPAN

Gives nice syntax, using Devel::Declare

curry add ($left, $right) {
return $left + $right;
}

Sub::Curried

on CPAN

Gives nice syntax, using Devel::Declare

curry (bwahaha!) add ($left, $right) {
return $left + $right;
}

Sub::Curried

Turn into something like

curry add {
return ROUTINE unless @_;
check_args(2, @_);
my $f = sub {
my $f = sub {
...
}
$f = $f->(shift) for @_;
return $f;
}

Sub::Curried

Turn into something like

curry add {
return ROUTINE unless @_;
check_args(2, @_);
my $f = sub {
my $f = sub {
...
}
$f = $f->(shift) for @_;
return $f;
}

Sub::Curried

add(5,6); # 11

Sub::Curried

add(5,6); # 11

add(5); # function that adds 5

Sub::Curried

add(5,6); # 11

add(5); # function that adds 5

add(); # function that adds...

Sub::Curried

add(5,6); # 11

add(5); # function that adds 5

add(); # function that adds...

i.e = \&add

Sub::Curried

Turn into something like

curry add {
return ROUTINE unless @_;
check_args(2, @_);
my $f = sub {
my $f = sub {
...
}
$f = $f->(shift) for @_;
return $f;
}

Sub::Curried

Give diagnostics if called with too many arguments

Sub::Curried

Turn into something like

curry add {
return ROUTINE unless @_;
check_args(2, @_);
my $f = sub {
my $f = sub {
...
}
$f = $f->(shift) for @_;
return $f;
}

Sub::Curried

Handleadd(1,2)

add(1)->(2)

Why?

Partial application

Why?

Automatic partial application everywhere

Why?

Automatic partial application everywhere

... turns out to be useful/elegant in Haskell

Why?

Automatic partial application everywhere

... turns out to be useful/elegant in Haskell

(actually, similar techniques are used in Perl)

Currying the Invocant

package My::Class
use base 'Class::Accessor';
__PACKAGE__->add_accessor('foo');
__PACKAGE__->add_accessor('bar');
__PACKAGE__->add_accessor('baz');

Currying the Invocant

package My::Class
use base 'Class::Accessor';
My::Class->add_accessor('foo');
My::Class->add_accessor('bar');
My::Class->add_accessor('baz');

Currying the Invocant

package My::Class
use base 'Class::Accessor';
My::Class->add_accessor('foo');
My::Class->add_accessor('bar');
My::Class->add_accessor('baz');

sub add_accessor {
my ($self, $accessor) = @_;
....

Currying the Invocant

Perl importing*{$CALLER::has} = \&has;

Currying the Invocant

Perl importing*{$CALLER::has} = \&has;

*{$CALLER::has} = mk_has();

Currying the Invocant

Perl importing*{$CALLER::has} = \&has;

*{$CALLER::has} = mk_has();

*{$CALLER::has} = has($CALLER);

Currying the Invocant

Moose!package My::Class;
use Moose; # imports has=has('My::Class')
has 'foo';
has 'bar';
has 'baz';

Currying the Invocant

Moose!package My::Class;
use Moose; # imports has=has('My::Class')
has 'foo';
has 'bar';
has 'baz';

(handwave)

Where were we?

my $add = \+; # FAIL

Where were we?

my $add = \+; # FAIL

my $add = op(+); # YAY

Where were we?

my $add = \+; # FAIL

my $add = op(+); # YAY

my $add2 = op(+2); # take THAT, Haskell!

But...

(-1)

But...

(-1)

(1-)

But...

(-1)

(1-) # minus(1)

But...

(-1) # minus(???)->(1)

(1-) # minus(1)

But...

(-1) # (flip(minus))->(1)

(1-) # minus(1)

Flip

curry flip ($fn, $left, $right) {
$fn->($right, $left);
}

Flip

curry flip ($fn, $left, $right) {
$fn->($right, $left);
}

Minus (3, 1) # 2

flip(minus)->(3, 1) # -2;

Flip

and with currying

my $prev = flip(minus)->(1);

$prev->(5);$left = 1

$right = 5

minus($right=5, $left=1); # 4

Sub::Section

More cool stuff

Sub::Section

my $greet = op(Hello .);

Sub::Section

my $greet = op(Hello .);

$greet->(World); # Hello World

Sub::Section

map op(*2), @numbers; (1,2,3,4)

> (2,4,6,8)

Sub::Section

map op(*2), @numbers; (1,2,3,4)

> (2,4,6,8)

Sub::Section

Perl map doesn't take functions...

Sub::Section

Perl map doesn't take functions...

map op(*2)->($_), @numbers; (1,2,3,4)

> (2,4,6,8)

(rant)

Perl's map is not functional

(rant)

Perl's map is not functionalWe took a feature from FP...
and made it not take functions.

(rant)

Perl's map is not functional

Perl's $_ is a horrible hack around not doing currying properly

(rant)

Perl's map is not functional

Perl's $_ is a horrible hack around not doing currying properly

Why am I still programming this silly language? ;-)

(yay!)

Perl's map is not functional

Perl's $_ is a horrible hack around not doing currying properly

Why am I still programming this silly language? ;-)

Oh yes! - because I can change it.

TODO: Functional::Map

fmap \&some_func, @list;

fmap sub { }, @list;

fmap op(+2), @list;

TODO: Functional::Map

fmap \&some_func, @list;

fmap sub { }, @list;

fmap op(+2), @list;

(those will all work anyway)

TODO: Functional::Map

fmap \&some_func, @list;

fmap sub { }, @list;

fmap op(+2), @list;

(those will all work anyway)

fmap { uc } @list;

TODO: Functional::Map

fmap \&some_func, @list;

fmap sub { }, @list;

fmap op(+2), @list;

(those will all work anyway)

fmap (bwahahaha!){ uc } @list;

TODO: Functional::Map

fmap \&some_func, @list;

fmap sub { }, @list;

fmap op(+2), @list;

(those will all work anyway)

fmap sub { uc }, @list;

TODO: Functional::Map

fmap \&some_func, @list;

fmap sub { }, @list;

fmap op(+2), @list;

(those will all work anyway)

fmap underscorify( sub { uc }),
@list;

TODO: Functional::Map

Dear lazyYAPC...

Sub::Section

my $contains_foo = op(=~/foo/);

More complex examples

centigrade = (fahrenheit-32)*5/9

More complex examples

centigrade = (fahrenheit-32)*5/9

my $f2c =
sub {
op(*5/9)->(
op(-32)->(shift))
};

More complex examples

centigrade = (fahrenheit-32)*5/9

my $f2c =
sub {
op(*5/9)->(
op(-32)->(shift))
};

MY EYES!

More complex examples

centigrade = (fahrenheit-32)*5/9

my $f2c =
sub {
op(*5/9)->(
op(-32)->(shift))
};

More complex examples

centigrade = (fahrenheit-32)*5/9

my $f2c =
sub {
op(*5/9)->(
op(-32)->(shift))
};

Never mind the boilerplate...

More complex examples

centigrade = (fahrenheit-32)*5/9

my $f2c =
op(*5/9) ();

Monadic programming is impractical in Perl...
only because of syntactic issues Mark Jason Dominus

http://perl.plover.com/classes/fp/samples/slide027.html

Monads made pretty

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

Monads made pretty

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

Source tree manipulation (B::OP magic)

Deparse and source text munging

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;
}

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

Optree munging

19: my $x o
t left_shift[t3] vK ->u
o padsv[$x:2078,2080]
sM/LVINTRO ->p
s entersub[t2] sKS/TARG,3 ->t
- ex-list sK ->s
p pushmark s ->q
q const(IV 2) sM ->r
- ex-rv2cv sK/130 ->-
r gv(*Just) s ->s
u nextstate(main 2079 b.pl:20)
v:*,&,$ ->v

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

Optree munging

19: my $x o
t left_shift[t3] vK ->u
o padsv[$x:2078,2080]
sM/LVINTRO ->p
s entersub[t2] sKS/TARG,3 ->t
- ex-list sK ->s
p pushmark s ->q
q const(IV 2) sM ->r
- ex-rv2cv sK/130 ->-
r gv(*Just) s ->s
u nextstate(main 2079 b.pl:20)
v:*,&,$ ->v

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

KABOOM

Source deparsing

Works surprisingly well

Source deparsing

Works surprisingly well

for trivial cases

(a bit fragile)

Source deparsing

Works surprisingly well

for trivial cases

(a bit fragile)

though localised to mdo { ... }

Devel::Declare

Even nicer syntax

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

Devel::Declare

And cuter implementation:

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

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;
}
...

Devel::Declare

No need to count nesting:

scope_inject

B::Hooks::EndOfScope

Use to inject a ; at the end of method declarations:

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

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
}

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
}

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
}

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

So...

We can now sequence commands!

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

So...

We can now sequence commands!

in pure Perl!

So...

OK, so this was big news in Haskell in 1990s

So...

OK, so this was big news in Haskell in 1990s

Imperative languages have always done this

What else can monads do ?

Sequencing

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

What else can monads do ?

Sequencing

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

What else can monads do ?

Sequencing

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

What else can monads do ?

Sequencing

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

Programmable semicolon!

Maybe

Success/Failure

mdo (Maybe) {
mbind $FH = m_open('