-
-
Save skids/c4a548c8e829cd4dd480 to your computer and use it in GitHub Desktop.
I did a bit of a dive into IRC logs on "implicit *%_". | |
I hope the following is a fair representation of arguments | |
for and against a spec-change. | |
TL;DR: Keeping or removing implicit *%_ will not prevent | |
people who prefer to do things "the other way" from doing so | |
anyway, though it may have a cultural impact and may affect | |
first impressions. Keeping it encourages agile styles | |
and hence ecosystem robustness but requires explanation of MMD | |
differences up front. Removing it kicks the MMD explanation | |
down the road but not by far since plenty of agile-style APIs | |
will be around even in the core (.new), and it encourages | |
a more fragile style WRT inter-package dependencies. On the | |
upside, removing it speeds debugging and prevents common | |
errors, and that is not a small upside. We really do want | |
to reduce the odds that we remove *%_ and then find out in | |
retrospect that that was a mistake, especially we do not | |
want to find that out after Christmas. | |
==== | |
There are two diametrically opposed design styles at work | |
in the "implicit named slurpy" debate. | |
We'll pretend for tersity that there are hardcore camps on | |
either side, while in reality most coders make use of both, | |
causing ambivalence during this debate. I think everyone will | |
agree that Perl 6 wants to support both styles (as we do | |
already with gradual typing.) | |
Let's avoid getting mired in design pattern nominclature and | |
call the two camps "Freedom" for those who prefer and use | |
the current behavior, and "Maturity" for those who would remove | |
the slurpy and/or would welcome opt-out warnings or errors | |
for unused named parameters. | |
Syntactically the stakes are actually small. Each camp has | |
an escape hatch they can (and will) use to get their desired | |
behavior out of Perl 6. | |
If "Freedom" wins, worst case (after some bugs are fixed), | |
the "Maturity" camp would then have to put a capture around | |
their method parameters to get their desired behavior. If | |
"Maturity" wins, "Freedom" merely has to put explicit slurpies | |
at the end of all their parameter lists. | |
Freedom loses: "method (:$a)" becomes "method (:$a, *%_)" for them | |
Maturity loses: "method (:$a)" becomes "method (| (:$a))" for them | |
The larger stakes are not really about the implicit named slurpy so | |
much as they are about where each design style should be encouraged. | |
The implicit named slurpy is just a point of contention in an | |
ongoing balancing act. | |
Larger stakes: | |
A) The impact on what kind of code Perl 6 encourages among coders | |
who are not set in their ways, starting with the first example | |
code new coders see and try to imitate. | |
B) The impact on -Ofun and the overall development experience. | |
C) The level of technical debt incurred in large projects. | |
D) The immediate short-term impact on the ecosystem. | |
E) The future impact on the ecosystem should the experiment fail | |
and be reverted. Especially were that to happen in 6.1 of | |
the language spec. | |
Some goals of each camp are now listed. They contend respectively | |
so do compare 1 with 1, 2 with 2, ... | |
"Freedom" camp goals: | |
1) Separation of concerns -- less impact of changes to superclass API. | |
Adding adverbs to methods requires no changes by your subclass | |
manintainers except for the rare name collision, as long as default | |
values preserve the original behavior. If they make sense, objects | |
of those subclasses can even start to use the new adverbs. | |
If someone from the "Mature" camp wants to "safely" use your module, | |
they can create a safety wrapper subclass which, likewise, will not | |
need to be changed unless the subclass wants to use the new adverb. | |
2) Rapid prototyping should be rapid, strictness gradual. | |
When you are quickly banging together a prototype, you do not want | |
to be bothered to comb the correct parameters out of a blob of | |
arguments and spoonfeed them to a method call, especially | |
when multiplexing between multiple similar APIs. Forcing users | |
of nextsame to pre-prune arguments and switch to nextwith | |
is a DRY violation. | |
For example, when you want to send logs to both a syslog server | |
and local file through a printf-like API, it would be nice if the | |
former ignored :$io and the latter ignored :$ipaddr so you can | |
just throw the same arguments at both of them. | |
3) Local defaults should be sensible for the most common use cases. | |
Nobody has argued that Mu.new(*%_) should not be the default .new | |
for good reason -- it is a default that makes sense. Many classes | |
consist mainly of just new and attributes, and .new + nextsame is | |
often the first thing encountered since classes that use positionals | |
in initializers are also extremely common. So people will need | |
to learn how MMD works with a named slurpy early on anyway. | |
While one could argue that the "most common use case" of objects | |
is just to encapsulate methods and attributes with no subclasses, | |
that is likely not to be the case as the ease of Perl6's OO system | |
is realized by people who normally eschew real OO. | |
The trend will be towards more inheritence/composition, and the | |
styles of methods that use nextsame/etc will become more common, | |
so if all methods do not behave the same by default, the question | |
of "why doesn't this multi method behave like .new or .clone?" | |
will replace "why doesn't multi method behave like multi sub?" | |
4) Perl 5's "objects are just hashes" creative anarchy preserved. | |
Part of the charm of Perl 5 was the simple model of "objects are | |
just hashes" which was easy to understand and use for quick prototypes | |
and even for less mature medium sized projects. While it is arguably a | |
liability when mixed with advanced methodologies on larger codebases, | |
Perl 6 should not completely turn its back on this cultural heritage. | |
Named parameter handling is where we currently allow a little anarchy, | |
and by "allow" we mean "do not even discourage." Issuing warnings | |
about unused named parameters in an opt-out fashion would have | |
a negative impact on -Ofun. (Opt-in might actually be handy, | |
but it should be opt-in from the end-user scope not from | |
the upstream module author's scope.) | |
5) Consistency with "adverbs are tweaks, positionals important" theme. | |
There is a general theme that runs through Perl 6 whereby named | |
parameters are *usually* just extra tweaks to base functionality. | |
The current system promotes a worldview where objects make themselves | |
easily interchangeable. A common and encouraged pattern is that | |
object methods which do not support an adverb are just projections | |
of a complex function onto the surface of a sensible default. | |
This helps the language gel. | |
It is really the non-presence of an implicit *%_ on subroutines | |
that is the tweak, but it is a good tweak in that it gives | |
people with strict-typing instincts someplace to feel at home. | |
"Maturity" camp's goals: | |
1) Safer failures instead of unintended consequences. | |
Subclasses will be immune to any adverb additions. There is | |
no chance of strange and surprising behavior due to collisions | |
between added adverbs and adverbs used locally in a subclass, | |
because things will explode rather than continue to run but | |
do wrong things. | |
If someone from the "Freedom" camp wants to use your | |
module in an "agile" fashion, they can write a wrapper class | |
to liberalize it. They will have to update the wrapper class | |
if something changes upstream, but only that class. | |
The powerful versioning/packaging system in Perl 6 will eventually | |
compensate for the resulting dependency fragility by allowing | |
subclasses to lock in the superclass API. | |
2) Better warnings/debugging, and easier code analysis. | |
If you typo the name of an optional named parameter it would | |
be nice if you got a good error message rather than mysterious | |
default values in still-running code. | |
Having named parameters explicit will help automated code | |
analysis tools, maybe even optimization, work better. | |
3) Hard to explain, learning curve especially WRT MMD. | |
Teaching new users the nuances of MMD is hard enough | |
to begin with and is a barrier to adoption, because while | |
MMD is really useful and powerful (and marketing for | |
it is good) it may not give people the best experience | |
when they first try to use it. | |
The effect of a named slurpy on MMD semantics is actually | |
rather profound, and is an unnecessary complication that | |
we should insulate new users from until they are comfortably | |
using MMD. | |
4) The current situation does not inspire adoption by professionals. | |
Professional developers coming from more strict languages | |
are likely to get hung up on the current behavior and use | |
it as a reason to stop considering Perl 6 for a project. | |
People who have historically not liked Perl 5 but are willing | |
to give Perl 6 a lookover will see the lack of safety as | |
a prelude to an ecosystem full of really bad code. | |
By encouraging "nextwith" over "nextsame" beginner users | |
are taught to appreciate the benefits of strictures. They | |
may decide to liberalize their code using escape hatches but | |
they will have gotten used to getting good error messages | |
so they will see the drawback to that. Overall ecosystem | |
code quality will improve as a result. | |
5) Perceived inconsistency between subs and methods, warts. | |
The difference between methods and subs is magical (in a bad way.) | |
There is less cognitive dissonance to seeing *%_ explcitly | |
present everywhere a named slurpy is present. The proposed | |
escape hatch of using :(|c ()) looks ugly by comparison. | |
Any proposed trait or syntax sugar is not likey to be as | |
self-explanatory as just having *%_ show up where it should. | |
Further the interaction of *%_ with Perl 6s conventions for | |
literal pairs versus named arguments agravates this situation. | |
Footnotes: | |
I did not find this particular argument convincing: | |
(Rhetorically) "Why not have an implicit *@_." | |
Answer: because there is drastic difference between | |
the collision space sibling classes would add parameters | |
into, versus a whole namespace in the named parameter case. | |
Mystery: | |
What was Larry referring to here? http://irclog.perlgeek.de/perl6/2015-04-28#i_10518560 |
Do either of these positions impact the caller?
Pragmatically, If there is a choice that makes more work for library callers than the other, I vote for whichever makes all styles simpler. For example, if one requires an extra set of parens or brackets or an extra pipe sign to use libraries, but the other doesn't. In that case, I think we should lean towards the one that makes the caller's life easier. I do not care if a library author has to go through a small amount of extra work to use her preferred style. I think it is most important to not confuse/annoy library callers with extra syntax when it can be avoided. If there is no difference, my opinion is not strong in either direction.
Theoretically, I am personally in the Maturity camp. I think it is more consistent and easier to remember that if you add an explicit signature you get explicit behavior. Implying *%_
when you no longer imply *@_
with an explicit signature seems inconsistent and confusing to me. I understand the reasoning for doing it, I just disagree. I can live with it remaining as-is, but I will dislike it.
The one impact I am aware of to the caller is the error message.
If the :(|c (...)) syntax is used, current error messages are like unto:
Too few positionals passed; expected 1 arguments but got 0 in sub-signature of parameter c
Currently the unnamed syntax ":(| (...))' is broken, but would also mention "sub-signature." Arguably it would not be a horrible thing if signatures that had one unnamed top-level capture were special-cased for error messages, so there is wiggle room there.
Oh, and if the caller introspected the signature (which would be a rare thing to do) they would see the capture.
Is there no way we could have our cake and eat it too?
That is, warn if and only if you pass a named arg which is not explicitly handled by the method or any of it's super-class candidates.
And make it work for the default
.new
too, e.g.: