Executive summary:
I suggest a signature prototype that all multis defined in or exported
to the current namespace must match (they match if the proto would allow
the same argument list as the multi, though the multi may be more
specific). Prototypes are exportable. Documentation tie-ins are also
suggested, ultimately allowing for documentation-only interface modules
which collect and re-export the interfaces of implementation modules
while providing high-level documentation and constraints.
Details:
Larry has said that programming by contract is one of the many paradigms
that he'd like Perl 6 to handle. To that end, I'd like to suggest a way
to assert that "there will be multi subs defined that match the
following signature criteria" in order to better manage and document the
assumptions of the language now that methods can export themselves as
multi wrappers. Let me explain why.
In the continuing evolution of the API documents and S29, we are moving
away from documentation like:
our Scalar multi max(Array @list) {...}
our Scalar multi method Array::max(Array @array:) {...}
toward exported methods:
our Scalar multi method Array::max(Array @array:)
is export {...}
"is export" forces this to be exported as a function that operates on
its invocant, wrapping the method call. OK, that's fine, but Array isn't
the only place that will happen, and the various exported max functions
should probably have some unifying interface declared. I'm thinking of
something like:
our proto max(@array, *%adverbs) {...}
This suggests that any "max" subroutine defined as multi in--or exported
to--this scope that does not conform to this prototype is invalid. Perl
will throw an error at compile-time if it sees this subsequently:
our Any multi method Array::max(Array @array: $x)
is export {...}
However, this would be fine:
our Any multi method Array::max(Array @array: :$x)
is export {...}
because the prototype allows for any number of named parameters.
The default behavior would be to assume a prototype of:
our proto max([EMAIL PROTECTED], *%namedargs) {...}
Which allows for any signature.
Any types used will constrain multis to explicitly matching those types
or compatible types, so:
our Int proto max(Seq @seq, *%adverbs) {...}
Would not allow for a max multi that returned a string (probably not a
good idea).
The goal, here, is to allow us to centrally assert that "Perl provides
this subroutine" without defining its types or behavior just yet.
Documentation/code could be written for the prototype:
=item max
=inline our proto max(@array, *%adverbs) is export {...}
C<max> takes an input sequence or array (C<@array>) and
returns the maximum value from the sequence.
Specific implementations of max may be defined which
allow comparators or other adverbs (C<%adverbs>) to
be defined.
=cut
I've invented the "=inline" POD keyword here as an arm-wave to
programming by contract (both Perl and POD read its argument). If it's
not liked, the proto could be duplicated both inside and outside of the
documentation as we do now. Kwid, when it comes to pass, could provide
similar mechanisms. Given this, an entire "interface-only" module could
exist as POD/Kwid-only, which isn't a bad thing given that pre-processed
bytecode will be what most people are loading anyway, and thus not
parsing the POD every time as in Perl 5.
There's also another interesting thing that we might or might not decide
to tack onto protos, which is that the "is export" tag on one could
cause the exporter mechanism to automatically export any "is export"
tagged subroutines from the current namespace that match this prototype,
even if they came from a different namespace. Essentially defining one
proto allows you to re-export any multis that you imported by that name.
This seems to me to be a better mechanism than a simple :REEXPORT tag or
the like on the "use", as it more explicitly targets the interfaces that
your module defines its own prototype for.
This produces a generic set of documentation for a module that might
only act as a re-exporter for other modules. e.g. the above might appear
in a module called CORE which is "use"d by the runtime automatically,
and uses various other modules like Math::Basic and List without any
explicit export tags, thus providing the minimal interfaces that Perl
promises. S29 could eventually be adapted as the documentation for the
prototypes in that module without having to actually document the
individual APIs of the rest of the Perl runtime.
In Perl 6, therefore, "perldoc perlfunc" would become "perldoc CORE" or
whatever we call that module.
This is only a first step to programming by contract, which has many
more elements than simply blending signatures into documentation
(assertions and other elements are also part of it), but I consider it
an important step in the process to becoming more PbC-aware.
Any thoughts?