> On Nov 25, 2016, at 7:59 AM, Jay Abbott <[email protected]> wrote:
> Why not just say that this doesn't affect the removal of types (i.e. they can 
> still be discarded) and add something to prevent specific types being 
> discarded even if they're not statically used?
> 
> ```swift
> @nodiscard SomeType
> ```
> 
> This way, rather than a protocol opting in that anything implementing it is 
> automatically `@nodiscard` a program or library would declare some types as 
> non-discardable and could safely say "there will be at least 1 SomeProtocol" 
> available" without saying what specific type it is in the public 
> interface/docs?

In language design, we usually find that semantic annotations (which tell us 
what the programmer is trying to do) are superior to imperative annotations 
(which order the compiler to do something without much explanation why).  They 
allow the implementation to provide a better programming experience (e.g. 
giving useful diagnostics about likely errors and selecting more thoughtful 
default behaviors), they're easier to apply sensibly to new language 
constructs, and they allow better optimization with fewer unintended 
side-effects.  All of that applies here as well.

In this case, because it's really *the conformance of a type to a specific 
protocol* that should not be stripped, tying the annotation to the protocol is 
the more semantic approach, and as expected, it has a number of benefits.  
Let's dig in to why.

First, if the annotation has to be on every conforming type, there's an easy 
error of omission that could lead to bugs.  The really unfortunate thing here 
is that it often *won't* lead to bugs, because you'll only miss it if the type 
is actually stripped, and there might be all sorts of reasons why a type that's 
"really" unused is not stripped — maybe the implementation isn't running the 
analysis at all (which it probably won't in unoptimized builds), or maybe the 
implementation of the analysis just isn't very good, or maybe you have test 
code that still uses the type, or maybe there's some old code still linked into 
the project.  We really don't want to encourage a situation where e.g. you have 
an old implementation of a library sitting around, and you really want to 
delete it but you can't because when you do it breaks the build (but only in 
release mode), and two years later someone actually has the time to look into 
it and figures out that it's because you were missing this annotation on a 
bunch of types.

In contrast, if the annotation is on the protocol, it's really easy to check at 
the point of iterating over a protocol's conformances that the protocol has the 
right attribute.  And in fact, we can use this iteration as the "root" use of 
all the conformances, so that e.g. if we can prove that nothing in the program 
actually iterates the conformances of that specific protocol, we can go back to 
dropping them.

Second, if the annotation is on types, it's unclear what exactly about the type 
can't be stripped.  Do we have to keep all of its code around just in case 
something unexpectedly links to it?  Do we have to keep around every protocol 
conformance it has?  What does that mean if we add the ability to synthesize 
protocol conformances, or imply conformances post-hoc from other conformances?

So that's why this belongs on the protocol, and I hope the discussion gives you 
an idea of how to approach similar problems in the future.

John.
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to