Slides for the talk you-did-what at lpw-2013
-
You
did
WHAT?
-
Written
last year
-
... they
kicked us
out early!
-
Oh well
-
Saves me
writing one
this year
-
You
did
WHAT?
-
$obj->foo;
-
What's
going
on?
-
Get the
class name
for $obj
-
Lookup 'foo'
via the MRO
-
MRO?
-
Method
resolution
order
-
mro.pm
-
mro::get_linear_isa
-
DFS
C3
-
$obj->SUPER::foo
-
is actually
-
$obj->Class::Of::Obj::SUPER::foo
-
Class::Of::Obj::SUPER
-
Reference to
SuperClass::Of::Obj
-
(well, it's
almost that)
-
Wait.
-
SuperClass::Of::Obj::foo
-
Fully
qualified
dispatch
-
$obj->next::method
-
Always
dispatches
C3
-
So that
is ...
-
package next;
sub method {}
-
Oooo.
-
(MRO::Compat
actually does
it that way
for 5.8.x)
-
curry.pm
-
How many
times have
you written
-
sub {
$obj->foo(@_)
}
-
sub {
$obj->foo(@args, @_)
}
-
Well ...
-
package curry;
sub AUTOLOAD {
(my $meth = $AUTOLOAD) =~ s/.*:://;
my ($obj, @args) = @_;
sub { $obj->$meth(@args, @_) }
}
-
my $cb = $obj->curry::foo;
-
my $cb = $obj->curry::foo(@args);
-
Worse
still
-
use Scalar::Util qw(weaken);
...
my $weak_obj = $obj;
weaken($weak_obj);
sub {
$weak_obj->foo(@args, @_)
}
-
BORING
-
Easy to
fuck up
-
my $cb = $obj->curry::weak::foo;
-
You can
also
do ...
-
$obj->$foo
-
What?
-
$foo can be
a method name
or a coderef
-
So ...
-
How many
times have
you written
-
if (
blessed($obj)
and $obj->can('foo')) {
-
Can't use
the curry
trick
-
Can't call method
"foo" on an
undefined value
-
So, instead ...
-
Safe::Isa
-
our $_can = sub {
return unless blessed($_[0]);
$_[0]->can($_[1]);
}
-
our @EXPORT = qw($_can);
-
use Safe::Isa;
...
if ($obj->$_can('foo')) {
...
-
(also provides
$_isa $_does
$_call_if_object)
-
Next!
-
$obj->$foo
-
$obj->${\foo()}
-
Huh?
-
foo() can
return a name
or a coderef
-
The ${}
derefs
-
my $meth = "clear_${thing}";
$obj->$meth;
-
$obj->${\"clear_${thing}"};
-
$obj->${\sub {
my ($self, ...) = @_;
...
}}
-
Inline
method
calls!
-
So, ok, what's
that going
to help with?
-
Importing ...
-
sub import {
...
require Foo;
Foo->import;
}
-
WRONG
-
import() methods
almost always
use caller()
-
sub import {
...
require Foo;
goto &Foo::import;
}
-
What if
you've got
two things
to import?
-
sub import {
...
require Foo;
Foo->export_to_level(1);
}
-
Only works
for Exporter
using things
-
Worse
still
-
pragmas!
-
strict
warnings
etc.
-
Affect
compilation
scope
-
sub import {
...
strict->import;
-
Damn
-
Anything that
works for
both?
-
sub import {
...
my $caller = caller;
eval qq{
package ${caller};
Foo->import;
}
}
-
caller
correct
for
exporters
-
compilation
scope
right for
pragmas
-
sub import {
...
my $caller = caller;
eval qq{
package ${caller};
Foo->import;
}
}
-
UGLY
-
Sufficiently
encapsulated
ugly is
indistinguishable
from beautiful
-
my $importer = eval qq{
package $target;
sub {
my \$m = splice \@_, 1, 1;
shift->\$m(\@_)
}
};
-
$importer->(import => @args);
-
So ...
-
sub _importer {
my $target = shift;
\($importers{$target} ||= eval qq{
package $target;
sub {
my \$m = splice \@_, 1, 1;
shift->\$m(\@_)
};
} or die "Couldn't build importer for $target: $@")
}
-
sub import::into {
my ($class, $target, @args) = @_;
$class->${_importer($target)}(
import => @args
);
}
-
Import::Into
-
use Import::Into;
sub import {
my $target = caller;
...
Thing1->import::into($target);
Thing2->import::into($target, @args);
}
-
Works for
modules
-
Works for
pragmas
-
(also provides
unimport::out_of)
-
I did
what?
-
Import::Into
Safe::Isa
curry
-
Import::Into
Safe::Isa
curry
(all already
on CPAN)
-
Hopefully
they'll be
useful
-
Sufficiently
encapsulated
insanity ...
-
Thank You
IRC:mst
mst@shadowcat.co.uk
@shadowcat_mst