> 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

Reply via email to