|There's more than one way to do things|
Contexts and Perl 6by John M. Dlugosz (Monsignor)
|on May 18, 2009 at 17:39 UTC||Need Help??|
I've been musing over "contexts". In Perl 5, we have scalar context, list context, and maybe one or two other obscure ones.
Perl 6 has lots. I'm musing on this question: Just what exactly is the concept of a context in Perl 6? It might be a lot cleaner and conceptually pure than what we have in Perl 5.
What it is, fundamentally, is information that propagates into functions, which can affect the behavior of the function if the code chooses to examine and act on it. In Perl 6, there are contextual (dynamically scoped) variables, so it's not unique that information should flow this way.
The reason a function cares is because it indicates what the caller will do with this information. Why return a list, laboriously gathered from a remote data base, if the caller is just going to render the list into a number, getting the count? Instead it can return the count directly.
So the context indicates how the caller will use the object. It appears already that contexts in Perl 6 are linked with types (or roles, read "abstract interfaces"). So, to what extent is context simply equal to the type the caller wants? Is there more to it than that?
Maybe the "more to it" is just the same thing going on in the parser. A language construct, like a function, can mean different things depending on the context.
So, should any type coercion be a context? That includes vacuous conversions. A function may have a declared return type, so is strongly typed. But the caller immediately coerces it to something else, either with syntax for that purpose, or implicitly by feeding it to another function or language construct that wants a particular type. That immediate coercion of the result, "you gave me T1, what I really wanted was T2" is the context, I think.
If functions are strongly typed, the caller knows he is getting back T1, rather than waiting to see what he got after it's been executed. Same difference. The body of the function is written with certain knowledge of returning a T1, but can check to ask, "what is the caller really wanting?" and obtain T2.
This means that "lots" of contexts, corresponding to the built-in main interface types that match various sigils, is an understatement. Every type can be what is wanted, in context. That makes the name, wanted, even more clear. Given that simple meaning, what else is needed? The parser has some awareness of the primitive contexts, too. That won't be affected by making any type appear in wanted. Likewise, the smart-match won't care that the actual wanted is a more concrete type than it's checking for.
This also opens up some new tricks, in that a detailed return type can be used to prepare the return value more concretely. That is, instead of returning a plain Array of Any, which needs to be checked in some detail before it can be assigned to the caller's Array of Car, the function can make the return value of the desired concrete type in the first place. This can go part way to addressing the container-of-subtypes problem.