[swift-evolution] [swift-evolution-announce] [Review] Require self for accessing instance members
Paul Cantrell
cantrell at pobox.com
Wed Dec 16 15:09:23 CST 2015
Please forgive the length of the treatise that follows. It touches on some strong feelings I have about code as information design.
┝━━━ What is your evaluation of the proposal? ━━━━┥
I strongly oppose this proposal.
Despite the author’s claims, it reduces the signal/noise ratio of code, and thus has a negative impact on readability which outweighs its marginal safety benefit.
Developer opinion is strong, but strongly divided. Can one answer work for everyone? Consider: Teams who like this proposal have easy recourse if it is rejected: they can enable a build-time lint checker which will makes look and feel almost exactly like a language feature. However, teams who prefer implicit self have no recourse if this proposal is accepted.
This is, and should remain, a matter of style and taste left to individual teams.
┝━━━ Does this proposal fit well with the feel and direction of Swift? ━━━━┥
No, despite the proposal’s arguments to the contrary.
• This proposal confuses verbosity with clarity.
In its support, the proposal quotes Swift API guidelines that talk about clarity at point of use taking precedence over brevity. Note, however, that those guidelines do not speak against brevity either; they merely call out obsessive code golf as a fool’s errand.
Clarity comes neither from brevity nor from verbosity, but from mindfulness about which information to express. Both excessive conciseness and excessive repetition injure clarity.
In several places, these very same Swift guidelines that warn against brevity for its own sake also identify brevity as a way of achieving clarity. For example, in the “Omit Needless Words” section, they state, “More words may be needed to clarify intent or disambiguate meaning, but those that are redundant with information the reader already possesses should be omitted.”
That section proceeds to recommend that this is bad:
allViews.removeElement(cancelButton)
… and this is clearer:
allViews.remove(cancelButton)
…because the cancelButton argument already is of type Element.
Take note! The guidelines consider information redundant, and thus inadvisable, merely because it is already present in inferred information that may not have ever been stated explicitly near the call site. A “self.” that adds no further information certainly runs afoul of this principle.
This brings us to the core of my rejection:
Anything that is widely repeated becomes invisible.
This truth shoots an arrow through the heart of the preposterous argument that “explicit > implicit, always.” Imagine how ridiculous our code would look if we truly took this to heart! Instead of “let x = 1 + 1”, we'd have “allocate(constant, stack_allocated, unsynchronized, 32 bits) → name(“x”, locally_scoped); x ← integer_addition(1, 1, min: -2^31, max:2^31-1, overflow: exception)” … or wait, no, even that leaves things such as the meaning of “integer” implicit. Explicit always better? Really? Shouldn’t we then have to state the Peano Axioms every time we use an integer? Even LLVM IR leaves some things unstated.
Language design is all about what we choose to make implicit. Explicitness has no inherent virtue, and does not by itself justify this proposal. Explicitness is useful only when it is not redundant. Mandating “self.” creates redundancy. Leaving it to programmer discretion allows clarity in all cases — both when clarity is explicit and when clarity is concise.
• Swift adds language features that reduce boilerplate; this proposal increases it.
(Yes, we’re still in the “Does this proposal fit well with the feel and direction of Swift?” section.)
Some languages, such as C and Java, show a preference for minimizing the number of language constructs, even if this comes at the expense of repetitive keystrokes — and repetitive reading. (Witness Java’s approach to properties, for example.)
Though that approach does have the advantage of keeping the language smaller, easier to learn, and more stable over time, Swift comes down firmly on the other side: it seeks to eliminate boilerplate, and is willing to add language features to accomplish this when the benefits are clear. For example: stored properties and their surrounding features (willSet, didSet, lazy, etc.), optional unwrapping conveniences (if let, ??, etc.), default struct initializers, default arguments, etc.
This proposal runs contrary to Swift’s dislike of boilerplate and senseless repetition.
• This proposal runs contrary to Swift’s state purpose of being useful in a wide variety of programming domains.
One of the surprising things that this C/C++/Java developer learned from Ruby is that there’s tremendous value when a language’s syntax can get out of the way to let library authors create domain-specific idioms.
Ruby’s primary technique for doing this is leaning on implicit self (and no-parens method calls; a different question!) to provide a scope in which, for example, code describing a domain model or a state machine or an HTTP router can use only language relevant to the task at hand. Swift as it stands is reasonably friendly to this approach.
Mandatory “self.” would rule out this sort of programming — or least many of the best technique for implementing it.
Chris L has stated that he’d like to see Swift work in a wide variety of programming contexts. This proposal runs counter to that goal.
┝━━━ Is the problem being addressed significant enough to warrant a change to Swift? ━━━━┥
Scope errors and name collisions are indeed a source of programmer error, and it’s worth considering ways to address them.
However, the specific problem this proposal addresses is very narrow. Because of Swift’s lowercase letter naming convention, its relative paucity of global variables and functions, and the relatively low use of method-local functions, 99% of the problem this proposal solves is confusing local variables with member variables. That specific error is already flagged in common cases by other compile-time sanity checks, such as nonsensical self-assignments (foo = foo), use of a local variable before its declaration, and initializers completing before all stored properties are initialized.
While these compiler checks clearly do not cover all accidental uses of a local func / var where a member was intended, they leave the remaining safety gap filled by this proposal fairly small.
If Swift were to address the problem of scope confusion, it should do so in a way that covers more than this narrow subset of mistakes.
┝━━━ If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those? ━━━━┥
I’ve worked in the following languages which have implicit self/this:
Ruby*
Java
C++
…and in the following languages which do not have it:
Objective-C
Javascript
Coffeescript
Python
(And gosh, did THINK C have it? I honestly can’t remember.)
My arguments above reflect my experiences with languages of both kinds.
* I realize that in the discussion thread, Ruby’s @ came up as an alternative to explicit self. However, @ ≠ self in Ruby; instead, it distinguishes member variables from methods — and Ruby properties implemented as methods. Wherever it is legal in Ruby to say “self.”, if there’s no name conflict, then it’s also legal to leave it out.
┝━━━ How much effort did you put into your review? A glance, a quick reading, or an in-depth study? ━━━━┥
Probably too much — or perhaps not enough into editing it down! :P
Cheers,
Paul
> On Dec 16, 2015, at 12:55 PM, Douglas Gregor <dgregor at apple.com> wrote:
>
> Hello Swift community,
>
> The review of “Require self for accessing instance members” begins now and runs through Sunday, December 20th. The proposal is available here:
>
> https://github.com/apple/swift-evolution/blob/master/proposals/0009-require-self-for-accessing-instance-members.md <https://github.com/apple/swift-evolution/blob/master/proposals/0009-require-self-for-accessing-instance-members.md>
>
> Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at
>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>
> or, if you would like to keep your feedback private, directly to the review manager.
>
> What goes into a review?
>
> The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:
>
> * What is your evaluation of the proposal?
> * Is the problem being addressed significant enough to warrant a change to Swift?
> * Does this proposal fit well with the feel and direction of Swift?
> * If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
> * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
>
> More information about the Swift evolution process is available at
>
> https://github.com/apple/swift-evolution/blob/master/process.md <https://github.com/apple/swift-evolution/blob/master/process.md>
>
> Cheers,
> Doug Gregor
> Review Manager
>
> _______________________________________________
> swift-evolution-announce mailing list
> swift-evolution-announce at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution-announce
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151216/a1ca37ec/attachment.html>
More information about the swift-evolution
mailing list