Author: larry Date: Fri Apr 7 11:53:34 2006 New Revision: 8607 Modified: doc/trunk/design/syn/S03.pod
Log: Reduce now defined directly in terms of list operators, possibly autogenerated. Modified: doc/trunk/design/syn/S03.pod ============================================================================== --- doc/trunk/design/syn/S03.pod (original) +++ doc/trunk/design/syn/S03.pod Fri Apr 7 11:53:34 2006 @@ -12,9 +12,9 @@ Maintainer: Larry Wall <[EMAIL PROTECTED]> Date: 8 Mar 2004 - Last Modified: 5 Apr 2006 + Last Modified: 7 Apr 2006 Number: 3 - Version: 17 + Version: 18 =head1 Operator renaming @@ -118,8 +118,9 @@ compile time with a message directing the user either to use C<~~> or C<~=> instead, or to put a space between if they really wanted to assign a stringified value.) -=item * Unary C<.> calls its single argument (which must be a method, or a -dereferencer for a hash or array) on C<$_>. +=item * "Unary" C<.> calls its single argument (which must be a method, or a +dereferencer for a hash or array) on C<$_>. (It's not really a unary operator, +so we put it in quotes.) =item * The C<..> range operator has variants with C<^> on either end to indicate exclusion of that endpoint from the range. It always @@ -146,11 +147,16 @@ =item * C<...> is a unary postfix operator that constructs a semi-infinite (and lazily evaluated) list, starting at the value of its single argument. +It should not have any trailing whitespace, or it will be confused with +a "long dot". -=item * However, C<...> as a term is the "yada, yada, yada" operator, -which is used as the body in function prototypes. It complains -bitterly (by calling C<fail>) if it is ever executed. Variant -C<???> calls C<warn>, and C<!!!> calls C<die>. +=item * However, C<...> where a term is expected is the "yada, +yada, yada" prefix operator, which is used as the body in function +prototypes. It complains bitterly (by calling C<fail>) if it is +ever executed. Variant C<???> calls C<warn>, and C<!!!> calls C<die>. +The argument is optional, but if provided, is passed onto the C<warn>, +C<fail>, or C<die>. Otherwise the system will make up a message for +you based on the context. =item * In addition, to the ordinary C<.> method invocation, there are variants C<.*>, C<.?>, and C<.+> to control how multiple parent methods @@ -207,7 +213,44 @@ my @a = (5,6); [*] @a; # 5 * 6 = 30 -The reduction associates the same way as the operator used: +A reduction operator really is a list operator, and is invoked as one. +Hence, you maybe implement a reduction operator in one of two ways. Either +you can write an explicit list operator: + + proto prefix:<[+]> ([EMAIL PROTECTED]) { + my $accum = 0; + while (@args) { + $accum += @args.shift(); + } + return $accum; + } + +or you can let the system autogenerate one for you based on the +corresponding infix operator, probably by currying: + + # (examples, actual system may define prefix:[**] instead) + &prefix:<[*]> ::= &reduce.assuming(&infix:<*>, 1); + &prefix:<[**]> ::= &reducerev.assuming(&infix:<**>); + +As a special form of name, the non-prefix notation, as in + + proto [foo] ([EMAIL PROTECTED]) { + ... + } + +or + + &[foo] ::= ... + +defines both the C<[foo]> reduce operator and the C<foo> infix operator. +Where appropriate, use of the infix form may be optimized like this: + + $a foo $b ===> [foo] $a, $b + $a foo $b foo $c ===> [foo] $a, $b, $c + ... ... + +If the reduction operator is defined separately from the infix operator, +it must associate the same way as the operator used: [-] 4, 3, 2; # 4-3-2 = (4-3)-2 = -1 [**] 4, 3, 2; # 4**3**2 = 4**(3**2) = 262144 @@ -217,85 +260,88 @@ [<] 1, 3, 5; # 1 < 3 < 5 -If fewer than two arguments are given, one MMD attempt is made to -dispatch to the operator anyway with whatever arguments are given. -If this multi-dispatch succeeds, the result becomes the result of the -reduce. - -Otherwise, if the dispatch fails, then if there is one argument, -that argument is returned. However, this default doesn't make sense -for an operator like C<< < >> that doesn't return the same type as it -takes, so these kinds of operators overload the single-argument case +If fewer than two arguments are given, a dispatch is still attempted +with whatever arguments are given, and it is up to the receiver of that +dispatch to deal with fewer than two arguments. Note that the proto +list operator definition is the most general, so you are allowed to define +different ways to handle the one argument case depending on type: + + multi prefix:<[foo]> (Int $x) { 42 } + multi prefix:<[foo]> (Str $x) { fail "Can't foo a single Str" } + +However, the zero argument case must of necessity be handled by the +proto version, since there is no type information to dispatch on. +Operators that wish to specify an identity value should do so by +specifying the proto listop. Among the builtin operators, [+]() +returns 0 and [*]() returns 1, for instance. + +By default, if there is one argument, the built-in reduce operators +return that one argument. However, this default doesn't make sense +for operators like C<< < >> that don't return the same type as they +take, so these kinds of operators overload the single-argument case to return something more meaningful. All the comparison operators return a boolean for either 1 or 0 arguments. Negated operators, return Bool::False, and all the rest return Bool::True. -If no arguments were given, and the dispatch to the 0-ary form fails, -the reduce as a whole fails. Operators that wish to specify an identity -value should do so by overloading the 0-ary variant. Among the builtin -operators, infix:<+>() returns 0 and infix:<*>() returns 1. Note that, -while the single argument form can MMD dispatch based on the type of -the single argument, the 0-argument form cannot. - This metaoperator can also be used on the semicolon second-dimension separator: [[;] 1,2,3] # equivalent to [1;2;3] -Builtin infix operators specify the following identity operations: +Builtin reduce operators return the following identity operations: - multi sub infix:<**> () { 1 } # arguably nonsensical - multi sub infix:<*> () { 1 } - multi sub infix:</> () { ??? } # reduce is nonsensical - multi sub infix:<%> () { ??? } # reduce is nonsensical - multi sub infix:<x> () { ??? } # reduce is nonsensical - multi sub infix:<xx> () { ??? } # reduce is nonsensical - multi sub infix:<+&> () { +^0 } # -1 on 2's complement machine - multi sub infix:{'+<'} () { ??? } # reduce is nonsensical - multi sub infix:{'+>'} () { ??? } # reduce is nonsensical - multi sub infix:<~&> () { ??? } # sensical but 1's length indeterminate - multi sub infix:{'~<'} () { ??? } # reduce is nonsensical - multi sub infix:{'~>'} () { ??? } # reduce is nonsensical - multi sub infix:<+> () { 0 } - multi sub infix:<-> () { 0 } - multi sub infix:<~> () { '' } - multi sub infix:<+|> () { 0 } - multi sub infix:<+^> () { 0 } - multi sub infix:<~|> () { '' } # length indeterminate but 0's default - multi sub infix:<~^> () { '' } # length indeterminate but 0's default - multi sub infix:<&> () { all() } - multi sub infix:<|> () { any() } - multi sub infix:<^> () { one() } - multi sub infix:<!=> ($x?) { Bool::False } - multi sub infix:<==> ($x?) { Bool::True } - multi sub infix:{'<'} ($x?) { Bool::True } - multi sub infix:{'<='} ($x?) { Bool::True } - multi sub infix:{'>'} ($x?) { Bool::True } - multi sub infix:{'>='} ($x?) { Bool::True } - multi sub infix:<~~> ($x?) { Bool::True } - multi sub infix:<!~> ($x?) { Bool::False } - multi sub infix:<eq> ($x?) { Bool::True } - multi sub infix:<ne> ($x?) { Bool::False } - multi sub infix:<lt> ($x?) { Bool::True } - multi sub infix:<le> ($x?) { Bool::True } - multi sub infix:<gt> ($x?) { Bool::True } - multi sub infix:<ge> ($x?) { Bool::True } - multi sub infix:<=:=> ($x?) { Bool::True } - multi sub infix:<===> ($x?) { Bool::True } - multi sub infix:<&&> () { Bool::True } - multi sub infix:<||> () { Bool::False } - multi sub infix:<^^> () { Bool::False } - multi sub infix:<//> () { undef } - multi sub infix:<,> () { () } - multi sub infix:<¥> () { [] } - -Any builtin operator not mentioned here does not have an identity -value. User-defined operators may of course define their own -identity values. Math packages wishing to find the identity value -for an operation can call C<infix:{$opname}()> to discover it. -(There is no explicit identity property.) + [**]() # 1 (arguably nonsensical) + [*]() # 1 + [/]() # fail (reduce is nonsensical) + [%]() # fail (reduce is nonsensical) + [x]() # fail (reduce is nonsensical) + [xx]() # fail (reduce is nonsensical) + [+&]() # +^0 (-1 on 2's complement machine) + [+<]() # fail (reduce is nonsensical) + [+>]() # fail (reduce is nonsensical) + [~&]() # fail (sensical but 1's length indeterminate) + [~<]() # fail (reduce is nonsensical) + [~>]() # fail (reduce is nonsensical) + [+]() # 0 + [-]() # 0 + [~]() # '' + [+|]() # 0 + [+^]() # 0 + [~|]() # '' (length indeterminate but 0's default) + [~^]() # '' (length indeterminate but 0's default) + [&]() # all() + [|]() # any() + [^]() # one() + [!=]() # Bool::False (also for 1 arg) + [==]() # Bool::True (also for 1 arg) + [<]() # Bool::True (also for 1 arg) + [<=]() # Bool::True (also for 1 arg) + [>]() # Bool::True (also for 1 arg) + [>=]() # Bool::True (also for 1 arg) + [~~]() # Bool::True (also for 1 arg) + [!~]() # Bool::False (also for 1 arg) + [eq]() # Bool::True (also for 1 arg) + [ne]() # Bool::False (also for 1 arg) + [lt]() # Bool::True (also for 1 arg) + [le]() # Bool::True (also for 1 arg) + [gt]() # Bool::True (also for 1 arg) + [ge]() # Bool::True (also for 1 arg) + [=:=]() # Bool::True (also for 1 arg) + [===]() # Bool::True (also for 1 arg) + [&&]() # Bool::True + [||]() # Bool::False + [^^]() # Bool::False + [//]() # undef + [,]() # () + [¥]() # [] + +User-defined operators may define their own identity values, but +there is no explicit identity property. The value is implicit in the +behavior of the 0-arg reduce, so mathematical code wishing to find +the identity value for an operation can call C<prefix:{"[$opname]"}()> +to discover it. -To call some other non-infix function as a reduce operator, you must +To call some other non-infix function as a reduce operator, you may define an alias in infix form. The infix form will parse the right argument as a scalar even if the aliased function would have parsed it as a list: @@ -303,6 +349,8 @@ &infix:<dehash> ::= postcircumfix:<{ }>; $x = [dehash] $a,'foo','bar'; # $a<foo><bar>, not $a<foo bar> +Alternately, just define your own C<< prefix:<[dehash]> >> routine. + Note that, because a reduce is a list operator, the argument list is evaluated in list context. Therefore the following would be incorrect: @@ -318,9 +366,6 @@ @args = (\%a,'foo','bar'); $x = [dehash] @args; -Note also that C<[.foo]> always means C<[$_.foo]>, never a reduce operator. -Infix operators are not allowed to start with a dot. - =head1 Junctive operators C<|>, C<&>, and C<^> are no longer bitwise operators (see L</Operator