Skip to content

Instantly share code, notes, and snippets.

@skids
Last active August 29, 2015 14:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skids/c4a548c8e829cd4dd480 to your computer and use it in GitHub Desktop.
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
@smls
Copy link

smls commented Aug 2, 2015

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.:

class A { has $!a };
class B is A { has $!b };
B.new(a => 5, b => 10);  # OK
B.new(a => 5, c => 10);  # WARNING

@zostay
Copy link

zostay commented Aug 2, 2015

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.

@skids
Copy link
Author

skids commented Aug 2, 2015

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment