> On Dec 21, 2017, at 2:17 PM, John McCall <rjmcc...@apple.com> wrote: > > >> On Dec 21, 2017, at 3:10 PM, Matthew Johnson <matt...@anandabits.com >> <mailto:matt...@anandabits.com>> wrote: >> >> >>> On Dec 21, 2017, at 2:06 PM, John McCall <rjmcc...@apple.com >>> <mailto:rjmcc...@apple.com>> wrote: >>> >>> >>>> On Dec 21, 2017, at 2:41 PM, Matthew Johnson <matt...@anandabits.com >>>> <mailto:matt...@anandabits.com>> wrote: >>>> >>>> >>>>> On Dec 21, 2017, at 1:26 PM, John McCall via swift-evolution >>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>> >>>>>> >>>>>> On Dec 21, 2017, at 2:03 PM, Jordan Rose via swift-evolution >>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>> >>>>>> >>>>>> >>>>>>> On Dec 20, 2017, at 12:35, Karl Wagner <razie...@gmail.com >>>>>>> <mailto:razie...@gmail.com>> wrote: >>>>>>> >>>>>>> >>>>>>> >>>>>>>> On 20. Dec 2017, at 19:54, Jordan Rose <jordan_r...@apple.com >>>>>>>> <mailto:jordan_r...@apple.com>> wrote: >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>>> On Dec 20, 2017, at 05:36, Karl Wagner via swift-evolution >>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>>> On 19. Dec 2017, at 23:58, Ted Kremenek via swift-evolution >>>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>>>>>> >>>>>>>>>> The review of "SE 0192 - Non-Exhaustive Enums" begins now and runs >>>>>>>>>> through January 3, 2018. >>>>>>>>>> >>>>>>>>>> The proposal is available here: >>>>>>>>>> >>>>>>>>>> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md >>>>>>>>>> >>>>>>>>>> <https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md>+1, >>>>>>>>>> it needs to happen (and ASAP, since it _will_ introduce >>>>>>>>>> source-breaking changes one way or the other). >>>>>>>>> >>>>>>>>> I think non-exhaustive is the correct default. However, does this not >>>>>>>>> mean that, by default, enums will be boxed because the receiver >>>>>>>>> doesn’t know their potential size? >>>>>>>> >>>>>>>> It's not always boxing, but yes, there will be more indirection if the >>>>>>>> compiler can't see the contents of the enum. (More on that below.) >>>>>>>> >>>>>>>> >>>>>>>>> That would mean that the best transition path for multi-module Apps >>>>>>>>> would be to make your enums @exhaustive, rather than adding “default” >>>>>>>>> statements (which is unfortunate, because I imagine when this change >>>>>>>>> hits, the way you’ll notice will be complaints about missing >>>>>>>>> “default” statements). >>>>>>>> >>>>>>>> Yep, that's going to be the recommendation. The current >>>>>>>> minimal-for-review implementation does not do this but I'd like to >>>>>>>> figure out how to improve that; at the very least it might be a >>>>>>>> sensible thing to do in the migrator. >>>>>>>> >>>>>>>>> >>>>>>>>> I do have some thoughts about how we could ease the transition (for >>>>>>>>> this and other resilience-related changes), but it’s best to leave >>>>>>>>> that to a separate discussion. >>>>>>>>> >>>>>>>>> The one thing I’m still not overly fond of is the name - I would like >>>>>>>>> us to keep the set of resilience/optimisation related keywords to a >>>>>>>>> minimum. “exhaustive” for enums feels an awful lot like >>>>>>>>> “fixed_contents” for structs - couldn’t we come up with a single name >>>>>>>>> which could be used for both? I don’t think anybody’s going to want >>>>>>>>> to use “exhaustive” for structs. >>>>>>>> >>>>>>>> The core team was very focused on this too, but I contend that >>>>>>>> "exhaustive" is not about optimization and really isn't even about >>>>>>>> "resilience" (i.e. the ability to evolve a library's API while >>>>>>>> preserving binary compatibility). It's a semantic feature of an enum, >>>>>>>> much like 'open' or 'final' is for classes, and it affects what a >>>>>>>> client can or can't do with an enum. For libaries compiled from >>>>>>>> source, it won't affect performance at all—the compiler still knows >>>>>>>> the full set of cases in the current version of the library even if >>>>>>>> the programmer is forced to consider future versions. >>>>>>>> >>>>>>>> I'm working on the fixed-contents proposal now, though it won't be >>>>>>>> ready for a while, and the same thing applies there: for structs >>>>>>>> compiled from source, the compiler can still do all the same >>>>>>>> optimizations. It's only when the library has binary compatibility >>>>>>>> concerns that we need to use extra indirection, and then >>>>>>>> "fixed-contents" becomes important. (As currently designed, it doesn't >>>>>>>> affect what clients can do with the struct at all.) This means that I >>>>>>>> don't expect a "normal" package author to write "fixed-contents" at >>>>>>>> all (however it ends up being spelled), whereas "exhaustive" is a >>>>>>>> fairly normal thing to consider whenever you make an enum public. >>>>>>>> >>>>>>>> I hope that convinces you that "fixed-contents" and "exhaustive" don't >>>>>>>> need to have the same name. I don't think anyone loves the particular >>>>>>>> name "exhaustive", but as you see in the "Alternatives considered" we >>>>>>>> didn't manage to come up with anything significantly better. If >>>>>>>> reviewers all prefer something else we'd consider changing it. >>>>>>>> >>>>>>>> Thanks for responding! >>>>>>>> Jordan >>>>>>>> >>>>>>> >>>>>>> When you say “libraries compiled from source”, what do you mean? >>>>>> >>>>>> - Other targets in your project >>>>>> - Source packages built through SwiftPM / CocoaPods / Carthage / other >>>>>> >>>>>> And I was being imprecise with the terminology, but also >>>>>> >>>>>> - Libraries built by someone else but designed to be embedded into an >>>>>> app, so that there's no chance of a different version showing up at >>>>>> run-time. >>>>>> >>>>>>> >>>>>>> As for whether its a resilience feature: actually it is completely a >>>>>>> resilience feature. The effects on switching are only side-effects; >>>>>>> really what “exhaustive” or “nonexhaustive” are saying is literally >>>>>>> that cases may be added later. Even if we added private cases, you >>>>>>> wouldn’t need to mark those enums as specially exhaustive or not; that >>>>>>> would be implied. It’s an accommodation for things which don’t exist >>>>>>> yet, so really, it is all about resilience IMO. >>>>>> >>>>>> "Resilience", as an admittedly fuzzily-defined term in the Swift >>>>>> project, specifically refers to what changes can be made without >>>>>> breaking binary compatibility >>>>>> <https://github.com/apple/swift/blob/master/docs/Lexicon.rst>. It does >>>>>> not refer to every change you can make to a library. (For comparison, >>>>>> adding a field to a struct is not source-breaking in Swift. We would >>>>>> like to make it not ABI-breaking either; that proposal's coming soon.) >>>>>> >>>>>> >>>>>>> >>>>>>> Anyway, as I see it, library authors in general ought to be happy about >>>>>>> this: >>>>>>> + Their libraries become safer by default, so they can make changes in >>>>>>> the future without having to worry about breakage >>>>>>> + It doesn’t affect your code inside of a module, so it only affects >>>>>>> types they already explicitly marked “public” >>>>>> >>>>>> That's the intent. >>>>>> >>>>>>> >>>>>>> The only people who lose are multi-module App developers, because they >>>>>>> are “library authors” who don’t need to care about evolution, and now >>>>>>> need to add attributes to things they wouldn’t have to before, or >>>>>>> suffer language and performance penalties. Their libraries become less >>>>>>> reusable and not resilient-by-default. >>>>>>> >>>>>>> For example, I have an App for which I wrote a cross-platform model >>>>>>> framework in Swift. When I compile it as a framework inside my App, it >>>>>>> is bundled there forever. However, I use the same code to build >>>>>>> libraries for Linux, which I would like to ship in binary form to >>>>>>> 3rd-parties. Am I supposed to litter my code with annotations to mark >>>>>>> those types as final, just to make the App fast and convenient to code? >>>>>>> What happens when I need to fix a bug and distribute an updated copy, >>>>>>> this means the 3rd-parties need to recompile (which they won’t do…). >>>>>>> >>>>>>> Typically, for such a problem, I would recommend using a static library >>>>>>> instead. But we don’t have those, and anyway they’re not always the >>>>>>> best thing these days. So that’s why I started a new thread about >>>>>>> creating a “@static” import, so App developers can go back to all the >>>>>>> conveniences they had before. >>>>>> >>>>>> There won't be a perf penalty, but yes, I do expect multi-module apps to >>>>>> use 'exhaustive' on most of their enums, because they don't need the >>>>>> futureproofing. Maybe this should have been mentioned more explicitly in >>>>>> the proposal. >>>>> >>>>> As a perhaps more long-term design note, I think modules ought to have >>>>> the ability to version-lock themselves to one or more of their >>>>> dependencies. They would still be required to obey access control as if >>>>> they were outside those dependencies, but we would suppress some of the >>>>> semantic consequences of being outside the module, such as the need to >>>>> assume non-exhaustiveness by default. >>>>> >>>>> That is, there would be two independent axes of library dependency: >>>>> source vs. binary and version-compatible vs. version-locked: >>>>> - a source dependency allows the compiler to take advantage of the >>>>> implementation of public entities when generating code >>>>> - a version-locked dependency allows the compiler to take advantage of >>>>> the implementation of public entities when enforcing semantics >>>>> >>>>> Apps would generally elect to primarily use version-locked source >>>>> dependencies because they're just pulling down source libraries (e.g. >>>>> from github) and are comfortable with updating their code if the library >>>>> changes. >>>>> >>>>> Source libraries on github would generally want to use version-compatible >>>>> source dependencies because version-locking would put their clients in >>>>> "library hell" if the locking didn't all agree. >>>>> >>>>> Binary dependencies could reasonably use either. >>>> >>>> This model aligns pretty well with what I would like to see. It prevents >>>> us from paying a penalty when we don’t need the benefits provided by a >>>> restriction. >>>> >>>> Relating this back to the current proposal, would you expect an app to >>>> have the ability to switch over an enum provided by a version-locked >>>> dependency that is not annotated with @exhaustive without requiring a >>>> default clause? >>> >>> Yes, and as we find other places where program semantics depend on knowing >>> the implementation, I would expect them to follow suit. >>> >>> My guess is that enum exhaustiveness is probably more prominent than any >>> other such feature, and maybe even more prominent than all of them put >>> together, but possible examples include: >>> - automatically deriving protocol conformances, which we hope will >>> eventually be something you can do for an arbitrary protocol >>> - any other kind of structural metaprogramming we might add >>> - maybe memberwise struct initialization if there are no explicit >>> initializers, although this is arguably an access control question (just as >>> public/open is) >>> - ownership-related features that might make sense to restrict to stored >>> properties, like expecting a struct property to have a stable address, or >>> destructuring a struct with pattern-matching >>> >>> Now, some of these things might be nice to allow even for resilient types. >>> I know Joe has suggested adding some way of resiliently describing a >>> structural decomposition of a type, which you could then use to derive >>> conformances, etc. But since the basic motivation for restricting any of >>> them is source/binary compatibility, and since version-locking would tell >>> us that the programmer doesn't care about that, it seems sensible that >>> version-locking ought to suppress the restrictions. >>> >>>> Relating to @inlinable proposal also under review, would everything in a >>>> source dependency be automatically inlinable whether they were annotated >>>> as such or not (at least when version-locked)? >>> >>> Yes, and regardless of being version-locked. Inlining isn't semantically >>> visible: it's observable in various low-level ways, but it's not supposed >>> to affect basic program semantics except in incidental ways, e.g. by >>> lowering the memory requirements so that programs start working that didn't >>> before. So the compiler is generally always allowed to inline when the >>> call is direct and the callee has a known implementation; that's just >>> standard "as if" behavior. The fact that we can't do this today is just an >>> unfortunate consequence of our current build model. >> >> This is all exciting to hear (as a long term direction)! Thank you for the >> elaboration. > > To be clear, I'm laying out my own vision for this, not an accepted core-team > direction. But if you think it's exciting, that's certainly helpful!
Of course. :) This model would eliminate some of the tradeoffs app developers are currently required to make so it’s a huge win if we can get there. I hope the rest of the core team will be on board when the time is right. > > John.
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution