Work on Tie::Constrained has had me slinging around a lot of small anonymous subroutines. Many of those are composed of similar parts, joined by && or ||. I began wanting an easy way to crank out new subs from a small set of old.
In mathematics, operators like addition are defined on functions by their action on the values the functions return. For example, (sin + exp)(x) is sin(x) + exp(x). I got the notion of overloading the && and || ops for a class of blessed subroutines, so that $foo && $bar would return a blessed sub { &$foo && &$bar }.
You may have already noticed what's wrong with that idea. The && and || operators are not overloadable! I think I can understand why, too. The logical operators are used to test for success of a constructor. How could that work if $baz = FunkOpera->new(...) or die $! returned sub { $baz or die $!}?
Well, every idea has its ups and downs. The bitwise operators, & and | are conceptually similar to the logical ones, and they are overloadable. They can stand in for their logical counterparts. That is not ideal. The overloaded operators will be parsed by perl as the bitwise ops. There is no ultra-low precedence form of the bitwise ops. Those shortcomings may force heavier use of grouping parentheses than we're accustomed to. It's not perfect, but it will do.
So here it is, called for the moment FunkOpera.
#!/usr/bin/perl package FunkOpera; use overload '&' => sub { my ($l, $r, $f) = @_; bless $f ? sub {&{$r} && &{$l}} : sub {&{$l} && &{$r}} , __PACKAGE__ }, '|' => sub { my ($l, $r, $f) = @_; bless $f ? sub {&{$r} || &{$l}} : sub {&{$l} || &{$r}} , __PACKAGE__ }, '!' => sub { my ($fn) = @_; bless sub { ! &{$fn}}, __PACKAGE__ }; 1;
package main; my $foo = bless sub { $_[0] =~ /foo/ }, FunkOpera; my $bar = sub {"bar"}; my %funk; @funk{qw/foonbar foorbar barnfoo barrfoo/} = ($foo & $bar, $foo | $bar, $bar & $foo, $bar | $foo); for (keys %funk) { print qq($_("food") returns ), $funk{$_}->('food'}, $/; print qq($_("ford") returns ), $funk{$_}->('ford'}, $/; } __END__ foorbar("food") returns 1 foorbar("ford") returns bar barnfoo("food") returns 1 barnfoo("ford") returns foonbar("food") returns bar foonbar("ford") returns barrfoo("food") returns bar barrfoo("ford") returns bar
It is only necessary that one sub in a simple expression is a FunkOpera. The rest is taken care of by overload.pm magic. The odd trinary construction in the overload subs for binary operators helps with that, as well as keeping argument order straight.
This works very well for arithmetic operators, too.
So, it's not perfect, but it does what I wanted.
After Compline,
Zaxo
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: FunkOpera: Abstracting Perl Operators
by chb (Deacon) on Feb 04, 2005 at 09:12 UTC | |
Re: FunkOpera: Abstracting Perl Operators
by dragonchild (Archbishop) on Feb 04, 2005 at 14:11 UTC | |
Re: FunkOpera: Abstracting Perl Operators
by Anonymous Monk on Feb 04, 2005 at 10:50 UTC | |
Re: FunkOpera: Abstracting Perl Operators
by fergal (Chaplain) on Feb 04, 2005 at 18:08 UTC | |
Re: FunkOpera: Abstracting Perl Operators
by jdporter (Paladin) on Feb 04, 2005 at 14:20 UTC | |
Re: FunkOpera: Abstracting Perl Operators
by gaal (Parson) on Feb 05, 2005 at 11:13 UTC |