This and other RFCs are available on the web at http://dev.perl.org/rfc/ =head1 TITLE Scope of Polymorphic References and Objects =head1 VERSION Maintainer: Syloke Soong <[EMAIL PROTECTED]> Mailing List: [EMAIL PROTECTED] Date: 15 Aug 2000 Version: 1 Number: 108 =head1 ABSTRACT Syntax for Polymorping and Scoping References =head1 DESCRIPTION This is a proposal to define the scoping and overloading of Perl references. The polymorphic signature is proposed to comprise the arglist members, candidate operators and the return list members. In other words, rather than the tradition of using just the head (java) or head+operator (c++), the proposal is for head+tail or head+operator+tail polymorphic signatures . =head1 IMPLEMENTATION For comprehension of this proposal, the following terms are held within the bounds of this document. The concept stays, notwithstanding. var $a; our $b; my $c; $d = 0; $a is defined to be referentiable as public. $b is defined to be referenced within protected bounds of a package . $c is defined to be referentiable only privately within a function. $d is defined implicitly to be referentiable privately within a file. =head2 A new object module declaration method Allow function arg list but continue to use @_; The following would be equivalent forms in Perl5 and Perl6, respectively: Package Pine::Tree::State; sub genesis { my ($pkg, $oak, $maple) = @_; my $a = {}; bless $a, $pkg; return $a; } Package Pine::Tree::State; var genesis:(\new); function new ($oak :int, $maple: double) { bless $a:const, genesis; our $world={}; return $a; } function aux ($spruce:int) { my $world = this->$world; # this is instance handle of self object # my $world = our->$world; # how about using our? # my $world = my->$world; # or what about my? } =head2 Overloading of references A reference may be overloaded with a search list, thus acting as a container. Refer to RFC89 for fixating or constraining the data type of a variable. my $a1:(int) = 3; my $a2 = 4; my $a3:(int); my $a:($a1, $a2, $a3, double); $a is constrained in the following precedence - references $a1 if an integer evaluation is done - references $a2 if a liberal evaluation is done - references double if a double evaluation is done - $a3 is unreachable because $a1 is precedent in the search list $a = 11; #equivalent to $a1 = 11; $b:(int) = $a ; #equivalent to int $b = $a1; $a = 11.1; $c:(double) = $a ; #equivalent to double $c = 11.1; $a = 11.1; $c:(varchar) = $a ; #equivalent to varchar $c = '11.1'; $a = 11.9; $d:(int) = $a ; #equivalent to int $c = 11; $a = 11.9 + 0.5 ; $ceiling:(int) = $a ; #equivalent to int $ceiling = 12; =head2 Overloading of object containers Package Pine::Tree::State; var genesis:(\new_a, \new_b, \new_c); var habakuk:(\new_b, \new_c); var getab:(\new_a, \new_b); function new_a ($oak int, $maple double) { return bless $a:const={}, genesis; } function new_b ($oak:(varchar,int), $maple) { bless $a={},genesis; bless $b={},habakuk; my $c={}; var $r:($a,$b,$c); return $r; } function new_c (varchar $oak, $maple, $pine) { bless $a:const={}, (genesis,habakuk); return $a } #!/usr/bin/perl use Package Pine::Tree::State; my $a:(varchar,int); my habakuk $pineapple = genesis Package Pine::Tree::State($a, 'Banana'); my $melon = genesis Package Pine::Tree::State($a, 'Banana'); Blessing genesis confers it a constructor container. genesis is overloaded with two function references stored in an array. Refer to RFC89 for fixating the data type of a variable. getab is an overloaded function container but not of constructors. new_c acts as a constructor through genesis or habakuk but not through getab. Matching a call to a member of a container is by means of a signature due to the combination of the function argument list and return list. RFC89 suitability and precendent rules apply to matching signatures. The order of the array dictates the search order to match a call to the members of an overloaded reference. habakuk is an alternate constructor within the package. new_b is an insteresting constructor/function. Can you theorise what happens when new_b is activated through constructor container genesis, or constructor container habakuk, or object container getab? Notice new_b has arglist member $oak:(varchar,int), while new_c has varchar $oak. Effects of type casting and contraint are propagated into the function body. Signature of new_b is invoked by the genesis of Pine::Tree::State by $pineapple. $new_b->$r is an overloaded reference. Return of $r is a polymorphic return. $pineapple invokes new_b through genesis but has a casted reception for habakuk. So even though it is genesis'ed it becomes a habakuk object. Meanwhile $melon becomes a genesis object by having the value of new_b->$r:$c returned to it. The liberal new_b->$r:$c is presented when a programmer desires to have a default return signature match. Presence of an arglist does not preclude the referentiability of @_; @_ allows variable number of arguments into a function. =head2 A new object module component exportability Package Pine::Tree::State; var $A; our $B; my $C; $D = 0; var getx = \get_x; our gety = \get_y; my getz = \get_z; var new : (\new_a, \new_b); our $a={}; function new_a ($oak:int) { bless $a, new; return $a } function new_b ($oak:int, $maple: double) { bless $a, new; var getz1 = \get_z1; our getz2 = \get_z2; my getz3 = \get_z3; function $get_a1 = {return 'a1'}; function $get_a2 = {return 'a2'}; function $get_b = {return 'b'}; function $get_c = {return 'c'}; var geta = ($get_a,$get_a2); our getb = $get_b; my getc = $get_c; var $h; our $i; my $j; $k = 0; return $a; } function new_b ($oak :int) { bless $a={}, new; return $a } function get_x{ my ($a,$b) = @_;} function get_y{ my ($a,$b) = @_;} function get_z{ my ($a,$b) = @_;} function get_z1{ my ($a,$b) = @_;} function get_z2{ my ($a,$b) = @_;} function get_z3{ my ($a,$b) = @_;} All functions are not accessible outside the file except that, get_x, get_z1, $get_a1, $get_a2, $h, $A are implicitly exported and referentiable by as use Package Pine::Tree::State; getx($a1,$b1); my $p1 = new Pine::Tree::State ($oakhurst, $maplest); $p1->getz1($a1,$b1); $p1->geta($a1,$b1); $p1->i = 0; $A = 1; get_y, get_z2, $get_b, $i, $B are implicitly protected to be used and referentiable only within the package as, Package Pine::Spruce::City; use Package Pine::Tree::State; function pining { gety($a1,$b1); my $p2 = new Pine::Tree::State ($oakhurst, $maplest); $p2->getz2($a1,$b1); $p2->getb($a1,$b1); $p2->j = 0; $B = 1; } get_z3, $get_c, $j are implicitly private and referentiable only within the function new_b as, getz3($a1,$b1); getc($a1,$b1); $j = 0; $C, $D and all the following functions are protected to be used within the file as, $C = 0; $D = 1; getz($a,$b); get_x($a,$b); get_y($a,$b); get_z($a,$b); get_z1($a,$b); get_z2($a,$b); get_z3($a,$b); Use and not using of "my" outside any function to declare a reference within a file is indistinguishable as in the case of $C and $D. All functions defined inside function new_b is accessible only within new_b using their original names. =head2 Head + Tail polymorphic signature An overloaded function list need not contain only references to functions but to variables as well. Matching a call to a member of an overloaded container is by means of matching the signature comprising both the function argument list and return list. function $a1($x: int) = {}; function $a2($x: int) = {my $k:double; return $k }; function $a3($x:int) = {my @k; return \@k }; my $b; my @c; my %d; function $a4($x:int) = {my $e:(int,\@,\%); return $e}; var abcd:($a1, $b, \@c, \%d, $a2, $a3,$e); The calls to the overloaded container abcd abcd(my $i:int = 3); #matches $a1 abcd(my $i:int = 3) double; #matches $a2 abcd(my $i = 'a') ; #matches $b abcd(my $i = [] ) ; #matches \@c abcd(my $i = {} ) ; #matches \%d Traditional overload matching using arglist alone has been unsatisfactory especially in needing to dictate the match using the LHS: my $j2 = abcd(my $i: int); #matches $a1 my $j2:double = abcd(my $i: int); #matches $a2 my $j3:\@ = abcd(my $i :int); #matches $a3 my $j4:(int,\@,\%) = abcd(my $i: int); #matches $a4 To avoid complexity, matching involving arrays and hashes should not descend beyond the first level. If an array is fixed as an array of ints or double: function k{ my $k:(double) = []; return $k;} my @k1:(int) = k(); #no match, returns undef my @k2:(double) = k (); #matches function k However if an array is fixed as an array of array, the second array level is not investigated for matches. function m1{ my $k:(\@,double) = []; return $k;} function m2 { my $n1 = [a:(int), b:(double)]; my $n:($n1,double) = []; return $n; } my m:(\m1,\m2); my $k1 = [a:(int), b:(double)]; my @k:($k1,double) = m (); #matches function m1 because m1 is first in the list m2 will be unreachable through container m() because m1 and m2 have similar function signatures, despite the fact that $n1 and $k1 match would dictate that m2() should be the correct signature match. But a match due to $n1 and $k1 would require descending the arrays referenced by $k1 and $k2. Descending arrays to match signatures would make it utterly impossible to avoid divergent complexities. Though it might be good for theoretical studies. =head2 Head:operator:tail polymorphic signature Package Pine::Tree::State; var popeye:(\new_a, \new_b); #Polymorphic head constructor container operator popeye:(\func_a1, \func_a2, \func_b, \func_c, \func_d); function new_a ($oak :int, $maple: double) { return bless $a:const={}, popeye} function new_b ($oak :varchar, $maple) { return bless $a:const={}, popeye} #Polymorphic head for any operators #Polymorphic signature is (int,double,const):liberal:liberal function func_a1 ($a:int, $b:double, int $c:const) {return my $abc} #Polymorphic head for any operators #Polysig is (int,double,const):liberal:int function func_a2 ($a:int, $b:double, int $c:const) {return my $abc:int} #Polymorphic head for +,- operators #Polysig is (int,double,const):(+,-):(int,varchar) function func_b ($a:int, $b:double, int $c:const):(+,-) { my $operators = @OPERATORS; # Const Array referencing const values of operators (_,-) my $operator_used = $OPERATOR; # Const value of operator invoked if ($operator_used == '+') { return my $abc:int} else { return my $abc:varchar} } #Polymorphic head for *,- operators #Polysig is (int,double,const):(*,/):double function func_c ($a:int, $b:double, int $c:const):(*,/) {return my $abc:double} #Polymorphic head for =,>, \qwertyuiop operators #Polysig is (int,double,const):(=,>,\qwertyuiop):double function func_d ($a:int, $b:double, int $c:const):(=,\qwertyuiop) {return my $abc:double} use Package Pine::Tree::State; #Normal equal operator in action during object construction: my popeye $lemon = popeye ($o:int, $m: double); my popeye $lime = popeye ($o:varchar, $m); #Redefined equal and plus operator actions: $lime = $lemon + 5; #Programmer defined operator in action: sub dofirst{} sub dosecond{} $lemon qwertyuiop $lime ? dofirst(): dosecond(); The problem with tail signature is when return returns a function which returns another function which returns another function .... The return signature should be determined only by the return statements within the constructor, otherwise it's presumed liberal and inconsequential. =head2 Scope of return reference does not affect scope of returned object; Package Pine::Tree::State; var genesis:(\new_a, \new_b, \new_c); function new_a ($oak :int, $maple: double) { var $a={}; bless $a, genesis; return $a; } function new_b ($oak varchar, $maple) { our $a={}; bless $a, (genesis,habakuk); return $a; } function new_c ($oak, $maple, $pine) { our $a; bless $a, (genesis,habakuk); return $a } #!/usr/bin/perl use Package Pine::Tree::State; our $newb = \new_b(); bless getabc : (\new_a, $newb, \new_c); The use of var, our or my should be inconsequential to the accessibilities of new_a, new_b or new_c since the refences are returned anyway. However - var $a, our $a and my $a become globally, package and function accessible respectively and subjected to interference as bounded by their scopes. new_a has its own globally accessible $a, while package $a is shared between new_b and new_c. A call to getabc from anywhere except packages with root Pine will implicitly preclude new_b() from the search list due to: our $newb; =head2 Instance references and shared references Package Package Math::Complex; var (re,im):(int,double); #globally accessible objects var $gcounter; #globally shared reference our $pcounter; #package shared reference #globally accessible container complex singly loaded with a constructor var complex:(\comp); #Polysig is (re,im):liberal:complex function comp (my $re:re, my $im:im) { # our references declared inside constructor function is shared within package # but only through the instance handle of the object our ($rev,$imv):(\re,\im); my $a={}; #instance hash return bless $a, complex; #return instance handle } var plus:(\plus0,\0plus1,\plus2); #overloaded container with three candidate functions var mult:(\mult0,\mult1,\mult2); #overloaded container with three candidate functions #polysig complex:liberal:complex function plus0 (my $u:complex) { my $re:re = this->plus($u); #looking for polysig complex:liberal:re my $im:im = this->plus($u);#looking for polysig complex:liberal:im my $ret:\complex = \this; $ret->$rev = $re; $ret->$imv = $im; return $ret; } #polysig complex:liberal:re function plus1 (my $u:complex) { #add $ref of this instance with $ref of instance $u return my $re:re = this->$rev + $u->$rev; # this is instance handle of self } #polysig complex:liberal:im function plus2 (my $u:complex) { #similarly return my $im:im = this->$imv + $u->$imv; } #polysig complex:liberal:complex function mult0 (my $u:complex) { my $re:re = this->mult($u); #looking for polysig complex:liberal:re my $im:im = this->mult($u);#looking for polysig complex:liberal:im my $ret:\complex = \this; $ret->$rev = $re; $ret->$imv = $im; return $ret; } #polysig complex:liberal:re function mult1 (my $u:complex) { return my $re:re = this->$rev * $u->$rev - this->$imv * $u->$imv } #polysig complex:liberal:im function mult1 (my $u:complex) { return my $im:im = this->$rev * $u->$imv + this->$imv * $u->$rev; } =head2 Complex numbers using head+tail polysignature Using Package Math::Complex defined previously, #!/usr/bin/perl use Package Math::Complex; my $M = complex Math::Complex(3,5.5); my $N = complex Math::Complex(7.5,2); my $C2:complex = $N->plus ($M) ;#looking for polysig complex:liberal:complex my $C1:complex = $M->mult ($N) ;#looking for polysig complex:liberal:complex =head2 Complex numbers using head:operator:tail polysignature #overloaded operator container with four candidate functions operator complex:(\replus,\implus,\remult,\immult); or #container hierarchically overloaded with four candidates operator complex:(\plus,\mult) ; var plus:(\replus,\implus); var mult:(\remult,\immult); #polysig complex:(+,-):complex function plus (my $u:complex):(+,-) { my $re:re = this $OPERATOR $u; #looking for polysig complex:(+,-):re; my $im:im = this $OPERATOR $u; #looking for polysig complex:(+,-):im; my $ret:\complex = \this; $ret->$rev = $re; $ret->$imv = $im; return $ret; } #polysig complex:(+,-):re function replus (my $u:complex):(+,-) { return my $re:re = $$OPERATOR=='+' ? this->$rev + $u->$rev : this->$rev - $u->$rev ; } #polysig complex:(+,-):im function implus (my $u:complex):(+,-) { return my $im:im = $$OPERATOR=='+' ? this->$imv + $u->$imv : this->$imv - $u->$imv ; } #polysig complex:(*,/):complex function mult0 (my $u:complex) { my $re:re = this $OPERATOR $u; #matching polysig complex:(*,/):re my $im:im = this $OPERATOR $u; #matching polysig complex:(*,/):im my $ret:\complex = \this; $ret->$rev = $re; $ret->$imv = $im; return $ret; } #polysig complex:(*,/):re function remult (my $u:complex) { return my $re:re = $$OPERATOR=='*' ? this->$rev * $u->$rev : this->$rev / $u->$rev ; } #polysig complex:(*,/):im function immult (my $u:complex) { return my $im:im = $$OPERATOR=='*' ? this->$imv * $u->$imv : this->$imv / $u->$imv ; } #!/usr/bin/perl use Package Math::Complex; my $M = complex Math::Complex(3,5.5); my $N = complex Math::Complex(7.5,2); my $C2:complex = $N+$M ;#matching polysig complex:(+,-):complex my $C1:complex = $M/$N ;#matching polysig complex:(*,/):complex =head2 Finally There's no business like Perl business. Is your polysig republican:liberal:democrat or reform:liberal:green ? A pardon for the author should be in order for having wildly introduced keywords and phrases. Full and enthusiatic support will be given to alternative keywords, phrases and any rearrangements that promote symmetric and concentric Perl. Another pardon should also be for having authored this RFC first but published later than RFC95. =head1 REFERENCES RFC 28: Perl should stay Perl. RFC 89: Controllable Data Typing.