> On Dec 21, 2017, at 3:10 PM, Matthew Johnson <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!
John.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution