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