One of us asked earlier this week how to replace object methods at runtime, and another of us wondered why. Here is an example.

Sometimes we care what numbers are divisible by some given divisor. We can say

# encapsulate the mechanics of divisibility testing use Divisor; my $seven = Divisor->new(7); ok( $seven->divides(42), 'Seven divides 42.' ); ok( not( $seven->divides(365) ), 'The year does not contain a whole number of weeks.', ); ok( $seven->divides( 400*365 + 97 ), 'But the Gregorian calendar repeats itself every 400 years.', );

As you might expect, Divisor has been implemented to convert strings to numbers in Perl's usual seamless way:

my $two = Divisor->new(2); ok( $two->divides('42'), "Two divides '42'." ); ok( not( $two->divides('19') ), "Two does not divide '19'.", );

Now for a contrived example.

my $long_string_of_twos = '2' x 320; TODO: { local $TODO = 'Redefine divisibility by two.'; ok( $two->divides($long_string_of_twos), "Two divides $long_string_of_twos.", ) or diag 'We just contrived an overflow exception.'; }

This should not require Math::Big. Runtime polymorphism to the rescue:

# just look at the last digit, silly! $two->set_divisibility_test( sub {shift =~ /[02468]$/} ); ok( $two->divides($long_string_of_twos), "Two divides $long_string_of_twos, as any fool can see.", );

So that's what runtime polymorphism might be good for. In this particular case, I like it better than the classic subclassing approach.

Before I insert an implementation of the Divisor interface, let me invite the brethren to show how it might be implemented using Moose or other fresh approaches.

Divisor.pm:

use strict; use warnings; package Divisor; use Carp; sub new { my ($class, $divisor) = @_; croak "Not an integer: $divisor" if $divisor != int $divisor; my $instance = bless \$divisor => $class; $instance->_init($divisor); } sub _init { my ($instance, $divisor) = @_; $instance->set_divisibility_test( sub { my ($dividend) = @_; my $remainder = $dividend % $divisor; return ($remainder == 0); } ); return $instance; } my %divisibility_test_for; sub set_divisibility_test { my ($self, $code_ref) = @_; croak "Not a CODE ref: $code_ref" if ref $code_ref ne 'CODE'; $divisibility_test_for{$self} = $code_ref; } sub divides { my ($self, $dividend) = @_; croak "Not an integer: $dividend" if $dividend != int $dividend; $divisibility_test_for{$self}($dividend); } 'Divide et impera!';

Divisor.t

use strict; use warnings; use Test::More tests => 13; use_ok 'Divisor'; my $seven = Divisor->new(7); isa_ok( $seven, 'Divisor' ); ok( $seven->divides(42), 'Seven divides 42.' ); ok( not( $seven->divides(365) ), 'The year does not contain a whole number of weeks.', ); ok( $seven->divides( 400*365 + 97 ), 'But the Gregorian calendar repeats itself every 400 years.', ); my $two = Divisor->new(2); isa_ok( $two, 'Divisor' ); ok( $two->divides('42'), "Two divides '42'." ); ok( not( $two->divides('19') ), "Two does not divide '19'.", ); my $long_string_of_twos = '2' x 320; TODO: { local $TODO = 'Redefine divisibility by two.'; ok( $two->divides($long_string_of_twos), "Two divides $long_string_of_twos.", ) or diag 'We just contrived an overflow exception.'; } can_ok( $two, 'set_divisibility_test' ); # just look at the last digit, silly! $two->set_divisibility_test( sub {shift =~ /[02468]$/} ); ok( $two->divides('42'), "Two divides '42'." ); ok( not( $two->divides('19') ), "Two does not divide '19'.", ); ok( $two->divides($long_string_of_twos), "Two divides $long_string_of_twos, as any fool can see.", );

output of Divisor.t

ok 1 - use Divisor; ok 2 - The object isa Divisor ok 3 - Seven divides 42. ok 4 - The year does not contain a whole number of weeks. ok 5 - But the Gregorian calendar repeats itself every 400 years. ok 6 - The object isa Divisor ok 7 - Two divides '42'. ok 8 - Two does not divide '19'. not ok 9 - Two divides 22222222222222222222222222222222222222222222222 +2222222222 2222222222222222222222222222222222222222222222222222222222222222222222 +2222222222 2222222222222222222222222222222222222222222222222222222222222222222222 +2222222222 2222222222222222222222222222222222222222222222222222222222222222222222 +2222222222 22222222222222222222222. # TODO Redefine divisibility by two. # Failed (TODO) test (Divisor.t at line 32) # We just contrived an overflow exception. ok 10 - Divisor->can('set_divisibility_test') ok 11 - Two divides '42'. ok 12 - Two does not divide '19'. ok 13 - Two divides 22222222222222222222222222222222222222222222222222 +2222222222 2222222222222222222222222222222222222222222222222222222222222222222222 +2222222222 2222222222222222222222222222222222222222222222222222222222222222222222 +2222222222 2222222222222222222222222222222222222222222222222222222222222222222222 +2222222222 22222222222222222222, as any fool can see.

In reply to Motivation for replacing object methods at runtime by Narveson

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.