Any modern web framework has to deal with the idea of routes and request routing somehow. Given a request path (such as /stocks/AA/view_analysis), how does your application know what to do?
Catalyst solves this elegantly with a feature known as chained actions. Controller methods can consume zero or more parts of the path but, when explicitly chained, can combine. Consider the example request path. The controller is Stocks.pm. The second component of the path (/AA) is the identifier for a stock (Alcoa, to be specific. I'm neither long nor short on Alcoa itself, though I probably own some shares as part of a fund somewhere.) The final component of the path, /view_analysis, is an action—a verb representing an action the controller should take on the object representing Alcoa in the system.
You can probably start to see the idea of the chain right away.
The Stock controller has a controller method called
view_analysis method chains off of the
it's already successfully dispatched to
view_analysis without a valid stock object
(Further, these methods are part of a chain which requires that users have successfully logged into the system; they chain off of a user authentication system.)
In code terms, the relevant attributes look something like:
sub authorized :Chained('/login/required') :PathPart('stocks') :CaptureArgs(0); sub get_stock :Chained('authorized') :PathPart('') :CaptureArgs(1); sub view_analysis :Chained('get_stock') :PathPart('view_analysis') :Args(0);
:Chained attribute is most relevant here.
:PathPart governs how Catalyst's dispatcher makes each method
visible to user requests (
get_stock doesn't consume a part of the
path on its own, while
authorized consumes the name of the
view_analysis consumes its own name).
:Args control how many other pieces
of the path the methods consume; in the case of
the single path element between
/stocks and any subsequent chained
actions—in this case,
the end point of a chain, you use
:Args instead of
With that all explained, request method chaining is fantastic. I can reuse
get_stock() for other request methods and get all of its benefits,
including the fact that only authorized users can even reach this point.
Yet I want to prove these characteristics of my application.
I want to prove these features so definitively that I don't want to write tests for them. I want my program to fail to compile if these characteristics are untrue.
I see chaining from
get_stock() as supplying an invariant
view_analysis() such that it proves, to my
satisfaction, that I can always rely on a valid stock object being available
within the analysis method. Always. Similarly, I can always rely on a valid
user being available within both methods. Always always.
The problem comes in that it's easy to make a typo in the name of a chain or
a method, or to use
:CaptureArgs instead of
Here's the thing: all of this metadata is metadata. All of this information is available at compile time, before Perl has to execute anything.
If I had a really good and extensible type system in Perl 5, I could write a couple of pieces of predicate logic to say that every chained method should be a starting point or have a valid predecessor. These are trivial properties of my program (no matter how large it gets) and they're resolvable with the information available at the point of compilation. Even with complex controller construction through the use of roles and parametric roles, this information is available.
I know how to emulate this behavior by injecting some sort of
CHECK block into the code and schlepping through the symbol table
and inspecting attributes myself, but that's emulating a useful feature we
could exploit in a lot of ways.
Forget the talk about making Perl into Java or C++ by adding a silly manifest static type system. We could find and fix real errors in logic—trivial errors, trivially discoverable—if we had an extensible type system which let us define our own simple predicates.
(Implementing such is left as an exercise for a small army of readers cloned from a very small army of brilliant p5p hackers with copious spare time and a habit of reading ACM papers before breakfast.)