<div dir="ltr">Hi there,<div><br></div><div>Just want to circle back on a few things.</div><div><br></div><div>You mentioned that library vendors communicate deprecation notices, but this is very prone to error. If someone misses the notice that a library puts out to communicate that the contracts of the enum have changed, this could break existing functionality they have already built. </div><div><br></div><div>You also mention that it's really hard for a library developer to predict how their enum will change, but it's even harder for a library developer to fully predict how a different user will use their library. As a developer, I don't want to have to look at the release notes to find out when a library has changed its contracts-- I want it to alert me in the most aggressive way possible to make sure that major changes to the library have been captured in my own code.</div><div><br></div><div>Jessie</div><div><div class="gmail_quote"><div dir="ltr"><div class="gmail_quote"><div style="word-wrap:break-word"><br><div><span><blockquote type="cite"><div>On 21. Sep 2017, at 00:51, Rex Fenley via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>> wrote:</div><br class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270Apple-interchange-newline"><div><div dir="ltr" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">Hi Jordan,<br><br>I've been keeping up with most of the discussion since I last emailed about my concern about making nonexhaustive the default mode for enums. So far I am still strongly in the camp of exhaustive by default.<br><br>Most of my understanding comes from the perspective of source compatibility; since I'm primarily an iOS app developer and contributor to open source through CocoaPods, I don't have enough experience with binary compatibility issues to understand the breadth of the effects of such a change on binary compatibility - so I will argue from the perspective of source compatibility. Additionally, all the following applies exclusively to enums from Swift and not Obj-C/C. Largely my concerns stem from the<a href="https://en.m.wikipedia.org/wiki/Default_effect_(psychology)" target="_blank"> "default effect</a>"- whatever default is chosen is going to unconsciously bias developers to use that default.<br><br>To start, I don't follow how nonexhaustive by default leads to more secure code or promises less. In a world where nonexhaustive is default, hard-to-track bugs will be introduced frequently into many Swift developer's code.<span class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270Apple-converted-space"> </span><br><ul><li>Evolving contracts - The change you suggest leaves it up to the user to remember to add new cases into their code if an enum ever does change in a framework, and their code relies on exhaustive pattern matching.<span class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270Apple-converted-space"> </span></li></ul></div></div></blockquote></span><div>I think this is the right thing to do. Library vendors can still communicate changes with clients through deprecation notices. Ultimately it is up to the vendor to maintain compatibility for people switching against those older cases, and up to the clients to adopt any new behaviour that gets implemented.</div><span><div><br></div><blockquote type="cite"><div><div dir="ltr" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><ul><li>Unhandled case bugs - This will lead to inconsistencies and hard-to-track bugs, since it would require the user to then track down an unhandled case that they may not even be aware exists (forcing googling, searching through documentation, etc.).</li></ul>This is something that I certainly have experienced in the past working across teams programming in Obj-C, and Swift has so far completely eliminated this class of bugs! In a world where exhaustive remains default, nothing changes, fewer bugs.<br></div></div></blockquote><div><br></div></span><div>I’m not sure that those issues are so difficult to track down. You’ll know that switch statements which fall-through to a default case are suspicious - but they already are today, too!. It’s not really any different to having allowing singular “if”s without a corresponding “else”. I could also see the debugging value in a “future” case label, but it’s not necessary IMO.</div><div><br></div><div>Although, I can kind of see your point:</div><div><br></div><div>If you’re talking about a private framework (e.g. the multi-module App scenario), then things are a little different for you because typically, you’ll ship new versions of the framework and App together. The framework can be as fragile as you like in that case - you can break all the ABI stability rules, because there will no apps importing the new version of the framework who were compiled against older versions of it. In many ways, those frameworks are really like static libraries (with extras, like resources).</div><div><br></div><div>It’s really only an issue for people whose frameworks are used across variously-distributed apps (your own, or other peoples’). The typical dynamic library use-case. Those people already know to be careful, so why make non-exhaustive the default for everybody? It’s a fair point.</div><div><br></div><div>Getting this right can be pretty hard. For example, I expect that @_inlineable functions within the enum’s declaring module will also be affected by this (a client could inline the code, then you add an enum case…). At the same time, it’s difficult for a shared library vendor to say definitively that a particular enum will never change (unless it really is a very simple thing, like NSComparisonResult) - almost anything with meaning to how the library works is usually subject to change in case edge-cases are discovered. The analysis of Apple’s frameworks is good data to prove it, but it also makes sense that vendors like to retain as much implementation freedom as possible.</div><div><br></div><div>So I’m in favour of the change, but absolutely not in favour of the keywords. We need to standardise a minimal set of keywords which broadly cover the things library authors need to care about (versioning, sub-typing, inlining, etc). How is “exhaustive” different from “final” or “@_versioned”? How do they interact? I think we could come up with a more coherent message.</div><span class="gmail-m_836116586989667577m_7422422391250117675HOEnZb"><font color="#888888"><div><br></div><div>- Karl</div><div><br></div></font></span><blockquote type="cite"><div><div class="gmail-m_836116586989667577m_7422422391250117675h5"><div dir="ltr" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br>Next off is developer usability. As someone who has contributed to several frameworks in Swift, one thing I know is that you can always count on people complaining about the usability of their library - it's something you learn to expect. If it turns out that people are very frustrated with an enum constantly changing and breaking compatibility, they will voice that concern with a thousand 👍 on github, debate will happen, and the appropriate course correction will be made. In this case, no real damage done.<span class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270Apple-converted-space"> </span><br><br>That said, if enums are nonexhaustive by default, frameworks will have more nonexhaustive enums (as it becomes convention). The class of bugs previously discussed will emerge, causing real damage to users and developers. Complaints will arise, but with more hostility. In this case, we end up back where we started, since framework developers will then mark exhaustive for all their enums. The only difference is that it'll be something that must be remembered to be done.<br><br>I can understand how someone developing an Apple framework may want nonexhaustive by default, since enums from some Apple libraries seem to be much larger and change relatively often. Yet, from my experience, this doesn't represent what you find in open source libraries that seem to land on something consistent and then stick with it. And that consistency pairs very well with exhaustiveness.<br><br>Given this, it's clear that adding a case to an enum as a source breaking change should be the expected behavior. It's safer for those using the frameworks, and back propagation from users will correct it if it becomes an annoyance. Nonexhaustive as a keyword is a nice additional tool to make this correction simpler (as well as protect C enums by default), but should not be the standard.<br><br>Best,<br><div>Rex</div></div><div class="gmail_extra" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br><div class="gmail_quote">On Fri, Sep 15, 2017 at 5:06 PM, Jordan Rose<span class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270Apple-converted-space"> </span><span dir="ltr"><<a href="mailto:jordan_rose@apple.com" target="_blank">jordan_rose@apple.com</a>></span><span class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270Apple-converted-space"> </span>w<wbr>rote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word">Hi, Rex. I definitely agree that 'exhaustive' is the right model for a multi-module app; indeed, there's no real reason for a single project to do anything else. However, it is not always the right behavior for libraries that actually get distributed, whether as source or as binary. In this case we want to minimize the error of omission: in the app case, forgetting "exhaustive" is an annoyance that you notice and fix once across your code base, but in the library case forgetting the "default case" means putting out a source-breaking release, and for libraries that have binary compatibility constraints there's no recourse at all.<div><br></div><div>While most of the proposal deals with the experience we've had with the Apple SDKs (as written in Objective-C), we actually<span class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270Apple-converted-space"> </span><i>have</i> run into this case in Swift already. The Swift Playgrounds app comes with a framework, PlaygroundSupport, that can be used from within a playground. It's important that when they upgrade the app, existing playgrounds don't break, since the end user may not have access to the entire code of the playground. (Remember that playgrounds are often authored by one developer or group, but then run and modified by someone else with a much lower skill level!) <i>That</i> means that PlaygroundSupport can't currently vend any enums that they expect playground authors to exhaustively switch over.</div><div><br></div><div>(And to make it even more specific—and appealing—one of the enums they were considering would be a representation of the Swift AST. This can obviously change from release to release, but previous switch statements should stay valid.)</div><div><br></div><div>Now, this is an example we know about, so we could certainly make it explicitly non-exhaustive. But in general we're in the same situation as 'open': if we want to be friendly to library authors, we need to make the default thing be the one that promises less, even if it means a bit of extra work in the "I-actually-own-everything" case.</div><div><br></div><div>Best,</div><div>Jordan</div><div><div class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270h5"><div><div><br></div><div><div><br><blockquote type="cite"><div>On Sep 15, 2017, at 15:47, Rex Fenley <<a href="mailto:rex@remind101.com" target="_blank">rex@remind101.com</a>> wrote:</div><br class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270m_-6879923890638754720Apple-interchange-newline"><div><div dir="ltr">Hey Jordan,<div><br></div><div>Thank you for the time writing this up. I've been following along to the discussion somewhat closely and have kept silent because `exhaustive` was originally set to be the default for enums. However, that changed and so I'd like to voice my opinion, I frankly don't like this idea.</div><div><br></div><div>At remind we use algebraic data types religiously for managing state and data and rely on exhaustive pattern matching to guarantee we're handling all states in our code. We're splitting out our code across modules and having this guarantee has been a joy to work with.</div><div><br></div><div>The benefit of making nonexhaustive the default for Swift 5 across all multi-module code (besides C code) seems minimal to me. If a developer feels like they're unnecessarily managing enum cases, they can simply add a `default` case whenever they please. This is already the case and I'm curious if there's every been any complaints about this and what they would be. I'd prefer to be cautious and force exhaustive pattern matching in all possible cases and leave it up to the developer to choose not to.</div><div><br></div><div>Ideally in my mind, these keywords won't be necessary. All Swift enums will remain as they are, exhaustively pattern matched by default. Enums from C code will be explicitly nonexhaustive in all cases.<br clear="all"><div><br></div>--<span class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270Apple-converted-space"> </span><br><div class="gmail-m_836116586989667577m_7422422391250117675m_-2605855186775200270m_-6879923890638754720gmail_signature"><div dir="ltr"><span><div style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-size:16px;font-family:Arial;background-color:transparent;font-style:italic;vertical-align:baseline;white-space:pre-wrap">Rex Fenley</span><span style="font-size:16px;font-family:Arial;background-color:transparent;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11px;font-family:Arial;color:rgb(153,153,153);background-color:transparent;vertical-align:baseline;white-space:pre-wrap">|</span><span style="line-height:1.15;font-family:Arial;color:rgb(153,153,153);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11px;font-family:Arial;color:rgb(153,153,153);background-color:transparent;vertical-align:baseline;white-space:pre-wrap">IOS DEVELOPER</span><br></div></span><span><br><div style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11px;font-family:Arial;color:rgb(153,153,153);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"><img src="https://lh5.googleusercontent.com/xMgzw3JkFL3DLkdwyq0WxJzKs_XP57gVVCaBMvgi1FKCjSeue0xdx3JZeCWBlxN4KRHhHOfdvJbc1N-AjTwXcKIq4cjJg9H7iaFpQ8WbO4N3c9Y5dzi19cPOs_owPquuqw" width="250px;" height="53px;" style="border: none;"></span></div><div style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><a href="https://www.remind.com/" style="text-decoration:none" target="_blank"><span style="font-size:11px;font-family:Arial;color:rgb(17,85,204);font-weight:bold;text-decoration:underline;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Remind.com</span></a><span style="font-family:Arial;vertical-align:baseline;white-space:pre-wrap;background-color:transparent"> </span><span style="font-size:11px;font-family:Arial;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">| </span><a href="http://blog.remind.com/" style="text-decoration:none" target="_blank"><span style="font-size:11px;font-family:Arial;color:rgb(17,85,204);text-decoration:underline;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">BLOG</span></a><span style="font-size:11px;font-family:Arial;vertical-align:baseline;white-space:pre-wrap;background-color:transparent"> | </span><a href="https://twitter.com/remindhq" style="text-decoration:none" target="_blank"><span style="font-size:11px;font-family:Arial;color:rgb(17,85,204);text-decoration:underline;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">FOLLOW US</span></a><span style="font-size:11px;font-family:Arial;vertical-align:baseline;white-space:pre-wrap;background-color:transparent"> | </span><span style="font-family:Arial;vertical-align:baseline;white-space:pre-wrap;background-color:transparent"> </span><span style="text-decoration:underline;font-size:11px;font-family:Arial;color:rgb(17,85,204);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"><a href="https://www.facebook.com/remindhq" style="text-decoration:none" target="_blank">LIKE US</a></span></div></span></div></div></div></div></div></blockquote></div></div></div></div></div></div></blockquote></div></div></div></div><span class="gmail-m_836116586989667577HOEnZb"><font color="#888888"><span><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline">______________________________<wbr>_________________</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline">swift-evolution mailing list</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><a href="mailto:swift-evolution@swift.org" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" target="_blank">swift-evolution@swift.org</a><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" target="_blank">https://lists.swift.org/mailma<wbr>n/listinfo/swift-evolution</a></span></font></span></blockquote></div></div></div></div>
</div><br></div></div>