[swift-evolution] Swift phases and mis-timed proposals
cantrell at pobox.com
Tue Jun 13 00:07:01 CDT 2017
> On Jun 12, 2017, at 7:12 PM, Ted Kremenek <kremenek at apple.com> wrote:
>> On Jun 12, 2017, at 12:47 PM, Paul Cantrell <cantrell at pobox.com> wrote:
>>> On Jun 12, 2017, at 1:29 AM, Ted Kremenek via swift-evolution <swift-evolution at swift.org> wrote:
>>>> On Jun 11, 2017, at 4:47 PM, Erica Sadun via swift-evolution <swift-evolution at swift.org> wrote:
>>>> I think having a queue to submit "proposals for eventually", written when the inspiration is there, and having a core team review (say once a month or even once a quarter) of their viability for future Swift directions would be amazingly valuable.
>>> This is a good point. I think the concern about a queue is that ideas in it may still be subject to starvation if the queue gets too long. Ideas also can atrophy in their relevance as the language evolves but proposals stay in the queue. It then becomes a delicate matter when closing out old proposals. …
>>> The point about understanding “viable for future Swift directions” is key here. Viability really comes down to trajectory for the language. None of us are fully omniscient about what is coming in future releases, but we do have a sense of some of the priorities for the language that we need to tackle, balanced with what **kind** of changes are still acceptable to take into the language depending on the kind of disruption they cause for users, the tools we have to mitigate any pain with those changes, etc.
>> This touches on two related concerns I’ve long had about how swift-evolution has worked so far.
>> Concern #1 is that we consider proposals in relative isolation, on their own merits. That’s understandable and wise. Without this focus, we’d never reach conclusions about anything. However, this does tilt the process toward greedy optimization as we traverse the language space. I worry that there’s not enough attention to the big picture.
> I agree that some of the proposals have felt like steps of a hill-climbing algorithm.
> A few thoughts here:
> - In contrast to the greedy optimization analogy, the series of manifestos in the Swift 4 timeframe (ABI stability, String, Ownership) are intended to provide an umbrella of design for what will turn out to be related but separate SE proposals. The idea of these manifestos is to chart a direction for design and provide the basis for which the proposals related to the goals of the manifesto will be evaluated.
Yes, the manifestos have been a positive and successful approach, and very helpful when present. I mentioned ABI stability, but the string & ownership manifestos also deserve their own huzzahs!
> - Many SE proposals have been about language refinements, which fall into a category of topics that have been part of an ongoing design discussion for Swift for years — including long before Swift was publicly announced. While the proposals may sometimes feel disjoint, their evaluation is not necessarily so. Often for me proposals relate (directly or indirectly) to design discussions from previous versions of Swift (including pre-Swift 1) that help provide broader optics when evaluating the merit of the proposals. Sometimes when a proposal comes back for revision it incorporates those optics.
> - Sometimes (often?) refinements aren’t part of a grand design. They evolve in the mind space from usage of Swift. In other words, greedy optimization is sometimes just a natural way discussion and design happens. The question in my mind is when these concerns come up should we throttle individual proposals and take more time to take a broader view? Maybe that’s the answer. Having been in discussions in the Core Team meetings I can say that sometimes it is clear there is a broader topic to be discussed, and other times it isn’t.
Perhaps the solution is not necessarily throttling proposals per se, but having some mechanism for routing a proposal to something other than either a review cycle or the freezer: “this needs manifesto-ing,” “this needs prototyping to measure impact on existing code,” “this needs to simmer and find its context before we work it into a proposal,” etc. (That’s related to Daryle’s original message.)
Jon’s idea of shunting some ideas to working groups seems like a good one.
>> Concern #2 is that it’s hard to know what to do with a proposal when the ideal answer is “we need to see how it plays out in practice and then decide whether to accept it.” Theoretical discussion untempered by practical prototyping is a great way for a group to talk itself into a bad idea. (This will especially be a problem with future work to build out generics & existentials.)
> I agree. Do you have specific thoughts here on what could be done differently?
I don’t have any brilliant ideas (or I’d have proposed them already), so I’ll throw out some dubious ones:
1. Variation on Jon’s “unstable feature” idea: Allow a proposal to be “provisionally accepted,” which means that the implementation stays on a branch pending hands-on evaluation. That might mean testing against the source compatibility suite, doing experimental migrations of existing projects to use the new feature, performance testing if performance was the concern, etc. Provisionally accepted SEs would get separate public snapshots before merging. Depending on concerns raised during review, those snapshots could be partial implementations, e.g. non-performant or serious known bugs, but would work enough to gather developer feedback. Voting for provisional acceptance would come with the implied (or explicit?) responsibility of helping with the provisional acceptance testing. Following such testing, the proposal would come back for better informed review.
2. Related idea: do partial implementations or static analysis to determine the frequency & extent of breakage in the compatibility suite _before_ things are floated as proposals, and include results in the “impact on existing code” section. It’s conceivable that, for example, it might be relatively easy to rough out a proposal’s effect on the type system without fully implementing it. However, I don’t know how realistic this is; I get the impression that the front of the Swift compiler pipeline is where a lot of the hardest and ugliest work lives.
3. Variation / related idea: build a toy version of Swift’s type system in Swift for experimentation and consistency checking. By “toy version,” I mean an easy-to-fiddle-with model of type rules that wouldn’t be suitable for a real compiler, but is robust enough to uncover the implications of type system changes. I’m thinking especially of future generics work here. (I did something along these lines as an undergrad, writing an SML model of the type system for a toy OO language. It was surprising how many inconsistencies I spotted just having to make that toy model pass SML’s type checker.)
These ideas seem to me problematic at best … but they’re what I was able to come up with.
FWIW, I thought the Great Renaming was a success, and succeeded because of iterative prototyping. It’s the sort of thing I have in mind with #2. Simply observing the effect of proposed naming conventions on the Cocoa APIs — without even being able to write code with them! — generated lots of useful discussion, and drove the naming conventions to a much better place than they started out. (I’m still relieved that the special exception for first argument labels disappeared, and that we didn’t end up with something like moveToX(_:y:). Phew!)
>> Swift is already a very featureful language, and not always in a good way. A new feature that makes good sense in the context of a particular problem may make less sense when we consider the totality of its effect on the programmer model: its mental burden, its unforeseen interactions, its surprising pitfalls. How often do we reviewers give only a perfunctory answer to “Does this proposal fit well with the feel and direction of Swift?” How often _can_ we give anything but?
>> The net result is that IMO we tend to over-favor narrow solutions that solve very specific problems, and under-favor simpler, more general features that are good-enough solutions to _many_ problems at once — not as nice for any _one_ of those problems in isolation, but nicer to work with taken as a whole.
>> The Great Access Modifiers Wars and the recent fussing of SE-110 fallout are good examples of these problems.
> I think the Great Access Modifier Wars benefit from a lot of hindsight 20/20. The question in my mind is how do we recognize when we are entering into such a topic again, and what steps do we take “consider the totality of its effect on the programmer model”, as you say? I have to say that we did *try* and consider the totality, or at least we thought we did, when we discussed the changes to access control. Further, it is sometimes hard to evaluate the full implications of a design change until they are unleashed. Myopia is always a risk in designing the ideal solution, with imperfect information or imperfect reasoning playing a factor in suboptimal decisions.
This is true. Any process that relies on perfect design up front is doomed. As we all well know.
> Many domains rely on iteration and refinement as a natural part of making progress given the limited capacity to understand everything up front. Language design really is no different, even though the stakes are sometimes higher because language design choices are so fundamental.
This is the crux of it, isn’t it? Compilers are hard to build, thus iterations are slow and costly. And if iterations span language releases, the cost of iteration is multiplied by the language’s user base. My groping about for suggestions above boils down to “make iterations less costly.” That is complementary to Jon’s working group suggestion, which runs more along the lines of “make fewer iterations necessary.”
>> The work on ABI stability is perhaps a good model for non-greedy planning guided by a coherent big picture.
> Indeed. The same goes for Ownership and String.
>> Do these musings make sense? I don’t have any clear answers, but hope the observations are helpful.
> They do — and I appreciate them!
> I do wonder how to apply some of these suggestions. With big efforts — like revising String — we know from the onset that there is a body of problems that need to be tackled and considered together. Not everything that pops up on swift-evolution is like that. For those topics that pop up on swift-evolution as an observation that something in the language isn’t as awesome as we want it to be, should we apply resistance to taking changes to provide more time to consider the broader context? Specifically, *how* do we make design decisions on such proposals, as you put it, less greedy?
Identifying when an isolated idea needs broader context is part of it.
Better weighing mental burden against benefit is part of it.
Part of it is mapping specific feature requests back to the _problem_ that’s motivating the request, and keeping a more globally visible pool of recurring pain points from which to identify bodies of related problems and perhaps see more cross-cutting solutions.
All of Jon’s ideas (idea generation venue, working groups, unstable features) could help with these things.
> Further, is this a rampant problem in your mind or a reaction to some decisions that went badly and had to be later re-considered? It’s easy to reflect on what hasn’t worked well, but any way we change the process has tradeoffs on what it yields.
“Rampant” is much too strong. It’s more a simmering concern, a vague unease.
Although I used meddlesome past proposals as examples, they’re not the only source of that unease. I’m particularly worried about future generics & existentials work. More generally, I’m concerned about the language growing in a piecemeal fashion that causes it to bulk up with quirks and arcane features, yet simultaneously shy away from the ambitious cross-cutting rethinking that keeps software elegant. It’s what I said in my previous message:
> The net result is that IMO we tend to over-favor narrow solutions that solve very specific problems, and under-favor simpler, more general features that are good-enough solutions to _many_ problems at once — not as nice for any _one_ of those problems in isolation, but nicer to work with taken as a whole.
I should clarify that I view the Swift evolution process as largely successful. Swift is evolving admirably, in leaps and bounds. Far more languages changes have been successful than not. Despite all the sound and fury on this list, and despite the fact that the core team still does most of the heavy lifting, community input has produced some great ideas, helped refine many a proposal, and caught oversights that would have been painful if released in the wild.
Swift’s mindful design is a defining feature of the language, and I wouldn’t want to mess with the core team’s process in a way that undermines that. Whatever it is that I’m trying to get at — and I’m not entirely sure myself! — it’s a light hand on the ship’s wheel and not a radical rethink of the whole evolution process.
More information about the swift-evolution