in reply to Is it ok to mix functional and oo programming in one package?

(I suspect you meant to say "imperative programming" rather than "functional programming", so I'm gonna run with that assumption.) There are plenty of examples of this behavior. (Forgive me if I don't name any.) I don't think it has to end up messy or troublesome at all. Here's a short example of one such setup.

In Foo.pm:

#!/usr/bin/perl use strict; use warnings; package Foo; sub new { my $class = shift; bless { name => 'foo' }, ref($class)||$class; } sub bar { my $ref = ref $_[0]; if ($ref eq __PACKAGE__) { ## OO method call oo_bar (@_); } elsif ($ref eq 'HASH') { ## imperative subroutine call with hashref as first element im_bar (@_); } } sub oo_bar { my $self = shift; print "oo_bar got object named ", $self->{name}, "\n"; } sub im_bar { my $hr = shift; print "im_bar got hashref with name ", $hr->{name}, "\n"; } 1;
In im-oo-test.pl:
#!/usr/bin/perl use strict; use warnings; use Foo; my $hr = { name => 'hashref' }; my $obj = new Foo; ## OO method call $obj->bar; ## imperative call, but since $obj is a blessed object, ## this is equivalent to the OO call Foo::bar($obj); ## imperative call Foo::bar($hr);
There's a zillion ways to proceed from this skeleton. You could use a naming scheme like Module::OO::sub() rather than oo_sub(), you could write one subroutine to handle both types of calls, you can make the ref $_[0] business a little cleaner with use UNIVERSAL 'isa'... you can even ignore whether a hashref is an object, provided it has the right keys and values in it.

I would consider it good programming practice to put maximum flexibility into how your subroutines may be called, as long as this flexibility doesn't introduce too much complexity on the backend. That's your call.

Replies are listed 'Best First'.
Re^2: Is it ok to mix functional and oo programming in one package?
by chromatic (Archbishop) on Oct 18, 2007 at 19:20 UTC

    There are some gotchas here.

    bless { name => 'foo' }, ref($class)||$class;

    The ref dance is completely unnecessary. Unless you're implementing a prototype-based OO system (and the rest of the constructor suggests that you're not), drop it.

    my $ref = ref $_[0]; if ($ref eq __PACKAGE__) {

    This just broke inheritance. If you really need to do this check, use Scalar::Util's blessed() or reftype().

    my $obj = new Foo;

    This syntax introduces weird parser ambiguities that often depend on the order of compilation. Stick with Foo->new().

    Foo::bar($obj);

    This breaks inheritance.

    ... you can make the ref $_[0] business a little cleaner with use UNIVERSAL 'isa'...

    That will break delegation and cognates and has the possibility of breaking inheritance. It may break overloading as well.

      my $obj = new Foo;

      This syntax introduces weird parser ambiguities that often depend on the order of compilation. Stick with Foo->new().

      According to the Camel Book beside me, method invocation using the METHOD CLASS_OR_INSTANCE LIST form is exactly equivalent to CLASS_OR_INSTANCE->METHOD (LIST) form.

        When it works, it is. The problem is that it doesn't always work. new Foo has two barewords, and whenever the Perl parser encounters barewords, it has to guess at what they are. Sometimes it has hints at what those barewords are (if it's encountered declarations of filehandles or subroutines) and it can guess correctly. Sometimes it doesn't have hints, because Perl embraces late binding.

        Sometimes it has hints which contradict late binding altogether. There's where you run into problems, because you have to know exactly which hints matter and when the parser knows about them to diagnose and correct the problem.

        Alternately, you could use a syntax which isn't ambiguous to the parser and has the benefit of clarifying what exactly happens (new not being a keyword in Perl as it is in some other languages with OO systems).

        To expand a bit on what chromatic said:

        The key words in "method invocation using the METHOD CLASS_OR_INSTANCE LIST form is exactly equivalent to CLASS_OR_INSTANCE->METHOD (LIST) form." are "method invocation".

        In other words, if new Foo is resolved by the perl parser as a method call it is exactly equivalent. The problem is that it isn't always. For instance, this works:

        #!/usr/bin/perl use warnings; use strict; my $a = new Foo; $a->hello; package Foo; sub new { return bless {},shift; } sub hello { print "hello"; }
        output:
        hello
        While this doesn't:
        #!/usr/bin/perl use warnings; use strict; sub new { print "haha"; return; } my $a = new Foo; $a->hello; package Foo; sub new { return bless {},shift; } sub hello { print "hello"; }
        output:
        Bareword "Foo" not allowed while "strict subs" in use at test.pl line +10. Execution of test.pl aborted due to compilation errors.

        Avoiding the:

        my $obj = new Foo;
        indirect object syntax is routine Perl style advice. See, for example: