develooper Front page | perl.perl6.language | Postings from September 2006

RFC: multi assertions/prototypes: a step toward programming by contract

Thread Next
From:
Aaron Sherman
Date:
September 27, 2006 10:48
Subject:
RFC: multi assertions/prototypes: a step toward programming by contract
Message ID:
451AB972.2090802@ajs.com
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(*@posargs, *%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?

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About