Title: Closures for Fun and Profit
1Closures for Fun and Profit
- Being a Lesson in what Closures are
- And a Demonstration of some Cool Shit using them
2Closures for Fun and Profit
To understand closures, first understand objects
An object is a blob of data with some code
associated with it
package Number sub new my(class, value)
_at__ bless value gt value , class
This is not an object
sub get my self shift() return
self-gtvalue sub set my self
shift() self-gtvalue shift()
sub add my self shift()
self-gtset(self-gtget() shift()) sub
subtract my self shift() self-gtset(self-gtg
et() - shift())
3Closures for Fun and Profit
use Number my foo Number-gtnew(42)
This is an object
4Closures for Fun and Profit
A closure is a blob of code with some data
associated with it
sub make_closure my data shift
return sub ...
sub make_closure my data shift
return sub my arg shift
sub make_closure my data shift
return sub my arg shift
return arg data
Subroutine gets a copy of the variable when it is
created - the code has some data associated with
it
even after make_closure exits!
(and this is why I think inside-out objects are
badly named)
5Closures for Fun and Profit
So lets use our closure
sub make_closure my data shift
return sub my arg shift
return arg data
This is not a closure
my add_2 make_closure(2) my add_5
make_closure(5)
This is a closure
my four add_2-gt(2) my fortytwo
add_5-gt(37)
6Closures for Fun and Profit
Wasnt that exciting!
Summary
An object is a relationship which lets you do
things to data
Access the code via the data
A closure is a relationship which lets you do
things with data
Access the data via the code
Computer Scientists may now leave the room
7Closures for Fun and Profit
Lucius Annæus Seneca (4 BCE - 65 CE) worlds
first software engineer
The way is made long by explaining, short and
efficient by example
Longum iter est per præcepta, breve et efficax
per exempla
The Death of Seneca, by Peter Paul Rubens
8Closures for Fun CPUEmulatorZ80
A Z80 emulator written in perl
On the CPAN
Make your code faster by writing time-critical
bits in assembler!
Andy Armstrongs fault
9Closures for Fun CPUEmulatorZ80
LD D, 0x12 LD E, 0x34
0x16 0x12 0x1E 0x34
0b00010110 0x12 0b00011110 0x34
_LD_r8_imm()
LD HL, 0xBEEF
LD DE, 0x1234
0x11 0x1234
0b00010001 0x1234
_LD_r16_imm()
10Closures for Fun CPUEmulatorZ80
Every register is an object CPUEmulatorZ80R
egister8 for 8-bit registers like D and
E CPUEmulatorZ80Register16 for 16-bit
registers like HL
_LD_r8_imm calls a Register8s set()
method _LD_r16_imm calls a Register16s set()
method
So what about DE? I need a Register16, and to
update two Register8s
11Closures for Fun CPUEmulatorZ80
package CPUEmulatorZ80Register16 called
as -gtnew( set gt sub , get gt ) sub new
my class shift bless _at__, class sub set
called by, eg, _LD_r16_imm()
my self shift if(exists(self-gtset))
self-gtset-gt(shift) else
self-gtvalue shift() 0xFFFF
Optionally pass in subroutines to use in the
get()/set() method, which are responsible for
accessing the real registers
12Closures for Fun CPUEmulatorZ80
package CPUEmulatorZ80 my self bless
create the CPU my register_DE
CPUEmulatorZ80Register16-gtnew( set gt
sub my value shift
self-gtregister(D)-gtset((value 0xFF00) gtgt
8) self-gtregister(E)-gtset(value
0xFF) )
Anonymous subroutine closes over self
Even when called from deep inside another object,
self still points back at the CPU object
13Closures for Fun CPUEmulatorZ80
my register_BC CPUEmulatorZ80Register16-gt
new( set gt sub my value shift
self-gtregister(B)-gtset((value
0xFE00) gtgt 8) self-gtregister(C)-gtset(
value 0xFF) ) my register_DE
CPUEmulatorZ80Register16-gtnew( set gt
sub my value shift
self-gtregister(D)-gtset((value 0xFE00) gtgt
8) self-gtregister(E)-gtset(value
0xFF) ) my register_BC_
CPUEmulatorZ80Register16-gtnew( set gt
sub my value shift
self-gtregister(B_)-gtset((value 0xFE00) gtgt
8) self-gtregister(C_)-gtset(value
0xFF) ) my register_DE_
CPUEmulatorZ80Register16-gtnew( set gt
sub my value shift
self-gtregister(D_)-gtset((value 0xFE00) gtgt
8) self-gtregister(E_)-gtset(value
0xFF) )
And then the code explodes
We need Refactoring-Man!
14Closures for Fun CPUEmulatorZ80
package CPUEmulatorZ80 my register_BC
self-gt_derive_register16(qw(B C)) my
register_DE self-gt_derive_register16(qw(D
E)) factory method sub _derive_register16
my(self, high, low) _at__ return
CPUEmulatorZ80Register16-gtnew( get
gt sub self-gtregister(high)-gtget() 256
self-gtregister(low)-gtget()
, set gt sub my
value shift
self-gtregister(high)-gtset(value gtgt8)
self-gtregister(low)-gtset(value
0xFF) )
You can close over multiple variables
15Closures for Fun CPUEmulatorZ80
Closures reduced the amount of code Fewer cut n
paste bugs Easier to update if a bug is found
16Closures for Profit SortMultipleFields
Inspired by something I needed at work and wrote
a much less elegant solution for
Sort lists of hashrefs
Also on the CPAN
17Closures for Profit SortMultipleFields
Lets you sort this data
author gt 'Wall, Larry', title
gt 'Learning Perl, , author gt
'Hoyle, Fred', title gt 'Black Cloud,
The' , author gt 'Clarke, Arthur C',
title gt 'Rendezvous with Rama' , author
gt 'Clarke, Arthur C', title gt 'Islands In
The Sky'
Like this
my _at_sorted mfsort author gt
'ascending', shortcut for sub _0 cmp _1
title gt 'ascending
_at_unsorted
18Closures for Profit SortMultipleFields
Or like this
my _at_sorted mfsort author gt 'asc',
title gt 'asc', year gt 'desc',
_at_unsorted
colour gt sub my _at_in map
_ eq 'red' ? 0 _ eq
'orange' ? 1 _ eq 'yellow' ? 2
_ eq 'green' ? 3 _ eq
'blue' ? 4 _ eq 'indigo' ? 5
6 _at__
in0 ltgt in1
19Closures for Profit SortMultipleFields
mfsorts heart
my sortsub mfsortmaker(spec) _at_records sort
sortsub-gt(a, b) _at_records
sub mfsortmaker my spec shift my _at_spec
spec-gt() my sortsub sub() 0
default is to not sort at all - a No-Op
while(_at_spec) eat this from the end towards
the beginning my(spec, field)
(pop(_at_spec), pop(_at_spec)) if(!ref(spec))
got a string, turn it into a function-ref
spec (spec /asc(ending)?/i)
? sub _0 cmp _1
... my oldsortsub sortsub
sortsub sub()
spec-gt(_0-gtfield, _1-gtfield)
oldsortsub-gt(_0, _1)
extra layer of wrapping seems to prevent
segfaults in 5.8.8. WTF? return sortsub
return sub() sortsub-gt(_at__)
20Closures for Fun and Profit
Summary
A closure is a more codey sort of object
They are not magic
(merely sufficiently advanced technology)
They are not scary
You can do a lot with very little code
Which means less debugging
(which is good, cos debuggings Hard)
So lets go shopping!
I recommend buying Higher Order Perl, ISBN
1558607013