[swift-evolution] Proposal: Split extensions into implementing methods and adding static functions Was: [swift-evolution-announce] [Review] SE-0164: Remove final support in protocol extensions

Xiaodi Wu xiaodi.wu at gmail.com
Wed Apr 19 22:01:35 CDT 2017


Neat. Seems like that'd be a bug.


On Wed, Apr 19, 2017 at 9:40 PM, Howard Lovatt <howard.lovatt at gmail.com>
wrote:

> As it stands at the moment splitting to multiple files doesn't work. EG:
>
> ModuleA
>
> A.swift
>
> public protocol P {
>
> func m() -> String
>
> }
>
> extension Int: P {
>
> public func m() -> String {
>
> return "AP.m"
>
> }
>
> }
>
> ModuleB
>
> B.swift
>
> public protocol P {
>
> func m() -> String
>
> }
> extension Int: P {
>
> public func m() -> String {
>
> return "BP.m"
>
> }
>
> }
>
> ModuleC
>
> A.swift
>
> import ModuleA
>
>
> func am(_ i: Int) -> String { return i.m() }
>
> B.swift
>
> import ModuleB
>
>
> func bm(_ i: Int) -> String { return i.m() }
>
> main.swift
>
> let i = 0
>
> print(am(i))
> print(bm(i))
>
>
> Gives the following errors when built:
>
> sunzero-ln:ModuleC lov080$ swift build
> Fetching /Users/lov080/Google Drive/Swift/Examples/Example Module
> Clashes/ModuleA
> Fetching /Users/lov080/Google Drive/Swift/Examples/Example Module
> Clashes/ModuleB
> Cloning /Users/lov080/Google Drive/Swift/Examples/Example Module
> Clashes/ModuleA
> Resolving /Users/lov080/Google Drive/Swift/Examples/Example Module
> Clashes/ModuleA at 1.0.0
> Cloning /Users/lov080/Google Drive/Swift/Examples/Example Module
> Clashes/ModuleB
> Resolving /Users/lov080/Google Drive/Swift/Examples/Example Module
> Clashes/ModuleB at 1.0.0
> Compile Swift Module 'ModuleB' (1 sources)
> Compile Swift Module 'ModuleA' (1 sources)
> Compile Swift Module 'ModuleC' (3 sources)
> /Users/lov080/Google Drive/Swift/Examples/Example Module
> Clashes/ModuleC/Sources/B.swift:3:38: error: ambiguous use of 'm()'
> func bm(_ i: Int) -> String { return i.m() }
>                                      ^
> ModuleA.Int:2:17: note: found this candidate
>     public func m() -> String
>                 ^
> ModuleB.Int:2:17: note: found this candidate
>     public func m() -> String
>                 ^
> /Users/lov080/Google Drive/Swift/Examples/Example Module
> Clashes/ModuleC/Sources/A.swift:3:38: error: ambiguous use of 'm()'
> func am(_ i: Int) -> String { return i.m() }
>                                      ^
> ModuleA.Int:2:17: note: found this candidate
>     public func m() -> String
>                 ^
> ModuleB.Int:2:17: note: found this candidate
>     public func m() -> String
>                 ^
> <unknown>:0: error: build had 1 command failures
> error: exit(1): /Applications/Xcode.app/Contents/Developer/Toolchains/
> XcodeDefault.xctoolchain/usr/bin/swift-build-tool -f
> /Users/lov080/Google\ Drive/Swift/Examples/Example\ Module\
> Clashes/ModuleC/.build/debug.yaml
>
> As you can see that despite each file A and B in ModuleC only importing
> one module each all the extensions apply even if not explicitly imported.
>
>   -- Howard.
>
> On 18 April 2017 at 11:47, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
>> Simple: you put the user code that needs the more accurate library in one
>> file and import only the more accurate library there, and you put the user
>> code that needs the more performant library in a separate file and import
>> only the more performant library there! Swift's devotion to file-based
>> organization presents endless avenues of flexibility!
>>
>> What you write is an argument for designing a more expressive import
>> and/or disambiguation feature, not for disallowing public retroactive
>> conformance. A proposal to rip out entirely the ability to vend public APIs
>> with extensions is simply not going to fly. The core team has committed to
>> a small standard library and multiple independent core libraries like
>> Foundation. This can only work because Foundation can greatly expand the
>> API of standard library types through extensions.
>>
>> Your proposal would undo that design decision and require folding
>> Foundation's functionality into the standard library, or rewriting the
>> entire Foundation overlay to encapsulate standard library types instead of
>> extending them. For example, NSString would have to be a separate type that
>> encapsulates String. Yet oodles of work have gone into making NSString
>> seamlessly bridge to String in the first place.
>> On Mon, Apr 17, 2017 at 20:05 Howard Lovatt <howard.lovatt at gmail.com>
>> wrote:
>>
>>> Comments in-line below
>>>
>>> -- Howard.
>>>
>>> On 17 Apr 2017, at 9:01 am, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>
>>> This continues to forbid use cases that are critical.
>>>
>>>
>>> I think "critical" is overstating the importance. Plenty of successful
>>> languages do not have extensions. Extensions have been discussed and
>>> rejected by successful languages. The .NET guidelines suggest considered
>>> cautious use. I have tried to encapsulate the best practice into a language
>>> feature.
>>>
>>>
>>> For instance, I am writing a library that vends additional conformances
>>> for Float and Double. Any numerics library would need to do the same.
>>>
>>>
>>> You need to consider this carefully because your numerics library might
>>> add a method sinh for example and the user of your library might be using
>>> other numerical libraries as well, one of these others might also provide
>>> sinh. Which is to be called in the user code by f.sinh? Suppose one library
>>> emphasises speed over accuracy and the other vice versa. You really want
>>> access to both versions in the user code. This is a situation I have come
>>> across a few times in numeric C, Java, and C++ with matrix libraries where
>>> code I have worked on has used multiple libraries in the same application
>>> for good reason.
>>>
>>> I think you would be better vending functions for things like sinh,
>>> rather than extending float with an additional function, and vending types
>>> for more complex things like matrices, rather than extending arrays with
>>> dot products for example. If you vend a type you can easily give access to
>>> the underlying type using composition rather than extension or inheritance,
>>> there is an example of this in the proposal just above the Justification
>>> section..
>>>
>>>
>>> Your design would eliminate all such libraries, which is a non-starter.
>>> I am not sure what defects you are trying to solve with this proposal.
>>>
>>> I am trying to make Swift more consistent, easier to learn, and to
>>> encourage third-party libraries.
>>>
>>>
>>> On Sun, Apr 16, 2017 at 17:51 Howard Lovatt <howard.lovatt at gmail.com>
>>> wrote:
>>>
>>>> @Brent,
>>>>
>>>> I have updated the proposal to address your concerns, in particular I
>>>> don't see that retrospectively adding methods and protocols has been
>>>> removed it has just had its ugly corners rounded. See revised proposal
>>>> below particularly the end of section "Retrospectively adding
>>>> protocols and methods" and new section "Justification".
>>>>
>>>> Hope this convinces you that the change is worthwhile.
>>>>
>>>> -- Howard.
>>>>
>>>> ====================================
>>>>
>>>> # Proposal: Split extension usage up into implementing methods and
>>>> adding methods and protocols retrospectively
>>>>
>>>>
>>>> ## Revision history
>>>>
>>>> | Version | Date               | Comment       |
>>>>
>>>> |---------|--------------|--------------|
>>>>
>>>> | Draft 1   | 11 April 2017 | Initial version |
>>>>
>>>> | Draft 2  | 13 April 2017 | Added support for post-hoc conformance to
>>>> a protocol - replaced static final extensions with final extensions |
>>>>
>>>> | Draft 3 | 17 April 2017 | Added justification section |
>>>>
>>>>
>>>> ## Introduction
>>>>
>>>>
>>>> Currently extension methods are confusing because they have different
>>>> dispatch rules for the same calling syntax. EG:
>>>>
>>>>
>>>>     public protocol P {
>>>>
>>>>         func mP() -> String
>>>>
>>>>      }
>>>>
>>>>     extension P {
>>>>
>>>>         func mP() -> String { return "P.mP" }
>>>>
>>>>         func mE() -> String { return "P.mE" }
>>>>
>>>>     }
>>>>
>>>>     struct S: P {
>>>>
>>>>         func mP() -> String { return "S.mP" }
>>>>
>>>>         func mE() -> String { return "S.mE" }
>>>>
>>>>     }
>>>>
>>>>     let s = S()
>>>>
>>>>     s.mP() // S.mP as expected
>>>>
>>>>     s.mE() // S.mE as expected
>>>>
>>>>     let p: P = s // Note: s now typed as P
>>>>
>>>>     p.mP() // S.mP as expected
>>>>
>>>>     p.mE() // P.mE unexpected!
>>>>
>>>>
>>>> Extension methods can also cause compatibility problems between
>>>> modules, consider:
>>>>
>>>>
>>>> In Module A
>>>>
>>>>     extension Int: P {
>>>>
>>>>         func m() -> String { print("A.m") }
>>>>
>>>>     }
>>>>
>>>>
>>>> In Module B
>>>>
>>>>     extension Int: P {
>>>>
>>>>         func m() -> String { print("B.m") }
>>>>
>>>>     }
>>>>
>>>>
>>>> In Module C
>>>>
>>>>     import A
>>>>
>>>>     import B // Should this be an error
>>>>
>>>>     let i = 0
>>>>
>>>>     i.m() // Should it return A.m or B.m?
>>>>
>>>>
>>>> This proposal cures the above two problems by separating extension
>>>> methods into two seperate use cases: implementations for methods and adding
>>>> methods and protocols retrospectively.
>>>>
>>>>
>>>> ## Implementing methods
>>>>
>>>>
>>>> If the extension is in the same file as the protocol/struct/enum/class
>>>> declaration then it implements the methods and is dispatched using a
>>>> Vtable. EG:
>>>>
>>>>
>>>> File P.swift
>>>>
>>>>     protocol/struct/enum/class P {
>>>>
>>>>         // func m() not declared in type since it is added by the
>>>> extension, under this proposal it is an error to include a declaration in a
>>>> type *and* in an extension
>>>>
>>>>     }
>>>>
>>>>     extension P {
>>>>
>>>>         func m() { print("P.m") } // m is added to the
>>>> protocol/struct/enum/class declaration
>>>>
>>>>     }
>>>>
>>>>
>>>> Same or other file
>>>>
>>>>     struct S: P {
>>>>
>>>>         override func m() { print("S.m") } // Note override required
>>>> because m already has an implementation from the extension
>>>>
>>>>     }
>>>>
>>>>     let p: P = S() // Note typed as P
>>>>
>>>>     p.m() // Now prints S.m as expected
>>>>
>>>>
>>>> Extensions in the same file as the declaration can have any access, can
>>>> be final, and can have where clauses and provide inheritable
>>>> implementations.
>>>>
>>>>
>>>> In a protocol at present there is a difference in behaviour between a
>>>> protocol that declares a method that is then implemented in an extension
>>>> and a protocol that just has the method implemented in an extension and no
>>>> declaration. This situation only applies to protocols, for
>>>> structs/enumerated/classes you cannot declare in type and implement in
>>>> extensions. The proposal unifies the behaviour of
>>>> protocol/struct/enum/class with extensions and prevents the error of a
>>>> minor typo between the protocol and extension adding two methods instead of
>>>> generating an error.
>>>>
>>>>
>>>> The implementation needed to achieve this proposal is that a value
>>>> instance typed as a protocol is copied onto the heap, a pointer to its
>>>> Vtable added, and it is passed as a pointer. IE it becomes a class
>>>> instance. No change needed for a class instance typed as a protocol.
>>>>
>>>>
>>>> ## Retrospectively adding protocols and methods
>>>>
>>>>
>>>> A new type of extension is proposed, a "final extension", which can be
>>>> either in or outside the file in which the protocol/struct/enum/class
>>>> declaration is in. EG:
>>>>
>>>>
>>>>     protocol P2 {
>>>>
>>>>         func m2P()
>>>>
>>>>     }
>>>>
>>>>     final extension S: P2 { // Note extension marked final
>>>>
>>>>         func m2P() { print("SP2.m2P") } // Implicitly final, completely
>>>> implements P2
>>>>
>>>>         func m2E() { print("SP2.m2E") } // Implicitly final, not an
>>>> existing method
>>>>
>>>>     }
>>>>
>>>>
>>>> Which are called as any other method would be called:
>>>>
>>>>
>>>>     let s = S()
>>>>
>>>>     s.m2P() // Prints SP2.m2P
>>>>
>>>>     s.m2E() // Prints SP2.m2E
>>>>
>>>>
>>>> A method added by a final extension is is implicitly final, as the name
>>>> would suggest, and cannot be overridden.
>>>>
>>>>
>>>> Notes:
>>>>
>>>>
>>>>   1. If the final extension adds a method, e.g. m2E, that method cannot
>>>> already exist. IE a final extension cannot override an existing method or
>>>> implement a protocol declared method that lacks an implementation unless it
>>>> also adds the protocol.
>>>>
>>>>
>>>>   2. If the final extension adds a protocol then it must implement all
>>>> the methods in that protocol that are not currently implemented.
>>>>
>>>>
>>>>   3. If the final extension is outside of the file in which the
>>>> protocol/struct/enum/class declaration is in then the extension and the
>>>> methods can only have fileprivate or internal access. This prevents
>>>> retrospective extensions from numerous modules clashing, since they are not
>>>> exported outside of the module.
>>>>
>>>>
>>>> When a type is extended inside a module with a final extension the
>>>> extension is not exported. For example:
>>>>
>>>>
>>>>     final extension Int: P2 {
>>>>
>>>>         func m2P() { print("Int.m2P") }
>>>>
>>>>     }
>>>>
>>>>
>>>> If an exported function uses Int, e.g.:
>>>>
>>>>
>>>>     public func f(_ x: Int) -> Int {
>>>>
>>>>         x.m2P()
>>>>
>>>>         return x
>>>>
>>>>     }
>>>>
>>>>
>>>> Then when used in an external module both the input Int and the output
>>>> Int are not extended with P2. However as the Int goes into f it gains P2
>>>> conformance and when it leaves it looses P2 conformance. Thus inside and
>>>> outside the module the behaviour is easily understood and consistent and
>>>> doesn't clash with other final extensions in other modules.
>>>>
>>>>
>>>> Taking the above example further an Int with P2 conformance is required
>>>> by the user of a library; then it can simply and safely be provided, e.g.:
>>>>
>>>>
>>>>     public class P2Int: P2 {
>>>>
>>>>         var value = 0
>>>>
>>>>         func m2P() { print("Int.m2P") }
>>>>
>>>>     }
>>>>
>>>>
>>>> This type, P2Int, is easy to write, one line longer than a final
>>>> extension, and can easily be used as both a P2 and an Int and does not
>>>> clash with another Int extension from another module.
>>>>
>>>>
>>>> ## Justification
>>>>
>>>>
>>>> The aim of Swift is nothing more than dominating the world. Using the
>>>> current, April 2017, https://www.tiobe.com/tiobe-index/ index of job
>>>> adverts for programmers the languages that are in demand are: Java 15.568%,
>>>> C 6.966%, C++ 4.554%, C# 3.579%, Python 3.457%, PHP 3.376%, Visual Basic
>>>> .NET 3.251%, JavaScript 2.851%, Delphi/Object Pascal 2.816%, Perl 2.413%,
>>>> Ruby 2.310%, and Swift 2.287%. So Swift at 12th is doing very well for a
>>>> new language and is already above Objective-C at 14th. However there is
>>>> obviously a long way to go and the purpose of this proposal is to help with
>>>> this climb.
>>>>
>>>>
>>>> A characteristic of many of the languages above Swift in the Tiobe
>>>> Index is that they have major third party libraries; for some languages
>>>> they are almost defined by their third part libraries, e.g. Ruby for Rails.
>>>> A major part of this proposal is to make extensions safe when using
>>>> multiple libraries from different venders. In particular final extensions
>>>> are not exported.
>>>>
>>>>
>>>> As part of Swift's goal of world domination is that it is meant to be
>>>> easy to learn by a process of "successive disclosure". The current
>>>> inconsistent behaviour of protocols and extensions hinders this process and
>>>> is a common gotcha for newbies. This proposal eliminates that problem also.
>>>>
>>>>
>>>> Extensions are not new in languages, they are part of the .NET
>>>> languages for example. Since .NET popularised extensions they have been
>>>> discussed by other language communities, particularly Java and Scala, and
>>>> in the academic community (normally termed the Expression Problem) however
>>>> they have not proved popular because of the problems they cause. Nearly all
>>>> languages have a strong bias towards keeping the language small and simple
>>>> and trade of the advantages of a feature against the disadvantages and the
>>>> feature only makes it into the language if it offers many advantages, has
>>>> few disadvantages, and is not heavily overlapping with other features. This
>>>> keeping it small and simple test is what extensions have failed in other
>>>> languages.
>>>>
>>>>
>>>> Experience from .NET can however be used to improve extensions. There
>>>> is some excellent advice https://blogs.msdn.microsoft.c
>>>> om/vbteam/2007/03/10/extension-methods-best-practices-
>>>> extension-methods-part-6/ written by the VB .NET team when they added
>>>> extensions to VB .NET. The best-practice advice can be summarised by the
>>>> following quotes from the reference:
>>>>
>>>>
>>>>   0. "In most real world applications these suggestions [the rest of
>>>> the suggestions] can (and quite frankly should!) be completely ignored."
>>>> This is an important observations, in your own code that is not intended
>>>> for reuse; go for it, use extensions. The proposal importantly still allows
>>>> this style of programming and in fact improves it by adding consistent
>>>> behaviour and syntax between protocols/structs/enumerated/classes.
>>>>
>>>>
>>>>  1. "Read the .NET Framework Class Library Design Guidelines." The
>>>> equivalent for Swift is lacking at this stage. Probably because third party
>>>> libraries are rare.
>>>>
>>>>
>>>>   2. "Be wary of extension methods." This recommendation is formalised
>>>> in the proposal by limiting final extensions to be fileprivate or internal.
>>>>
>>>>
>>>>   3. "Put extension methods into their own namespace." This
>>>> recommendation is formalised in the proposal by limiting final extensions
>>>> to be fileprivate or internal.
>>>>
>>>>
>>>>   4. "Think twice before extending types you don’t own."
>>>>
>>>>
>>>>   5. "Prefer interface extensions over class extensions." Translation
>>>> to Swift terminology provide default implementations for protocol methods.
>>>> The proposal encourages this by eliminating a major gotcha with the current
>>>> implementation, namely the proposal always dispatches via a Vtable to give
>>>> consistent behaviour.
>>>>
>>>>
>>>>   6. "Be as specific with the types you extend as possible."
>>>> Translation to Swift terminology provide default implementations for
>>>> protocol methods that extend other  protocols if there is a more specific
>>>> behaviour that is relevent. The proposal encourages this by eliminating a
>>>> major gotcha with the current implementation, namely the proposal always
>>>> dispatches via a Vtable to give consistent behaviour.
>>>>
>>>>
>>>> The proposal formalises these best practices from .NET whilst
>>>> increasing consistence and without loosing the ability to use extensions
>>>> heavily in your own one-off code to allow for rapid development. Most of
>>>> the best practices are for better libraries, particularly third party,
>>>> which is an important area for future Swift growth onto the server side.
>>>> This proposal actively encourages this transition to large formal server
>>>> side code without loosing the free wheeling nature of app code.
>>>>
>>>>
>>>> ## Possible future work (not part of this proposal)
>>>>
>>>>
>>>> This proposal will naturally allow bodies to be added to protocols
>>>> directly rather than via an extension, since under the proposal the
>>>> extension adds the declaration to the type so it is a small step to allow
>>>> the protocol methods to have an implementation.
>>>>
>>>>
>>>> In an opposite sense to the above adding bodies to protocols,
>>>> extensions could be allowed to add method declarations without bodies to
>>>> protocols.
>>>>
>>>>
>>>> The two above future work proposals, if both added, would add symmetry
>>>> to where declarations and bodies may appear for protocols.
>>>>
>>>>
>>>> ## In summary.
>>>>
>>>>
>>>> The proposal formalises the split use of extensions into their two
>>>> uses: implementing methods and post-hoc adding protocols and methods.
>>>> Syntax is added that clarifies the two use cases, the former are termed
>>>> extensions and must be in the same file as the type is declared, and the
>>>> latter are termed final extensions and can be in any file, however if they
>>>> are not in the type's file the they can only have fileprivate or internal
>>>> access.
>>>>
>>>>
>>>> Note the distinction between an extension in the same file and in a
>>>> separate file is consistent with the philosophy that there is special
>>>> status to the same file as proposed for private in
>>>> https://github.com/apple/swift-evolution/blob/master/proposa
>>>> ls/0169-improve-interaction-between-private-declarations-
>>>> and-extensions.md.
>>>>
>>>>
>>>> ===================================================
>>>>
>>>>
>>>> #Proposal: Split extension usage up into implementing methods and
>>>> adding methods and protocols post-hoc
>>>>
>>>>
>>>> Draft 2 (Added support for post-hoc conformance to a protocol -
>>>> replaced static final extensions with final extensions)
>>>>
>>>>
>>>> ## Introduction
>>>>
>>>>
>>>> Currently extension methods are confusing because they have different
>>>> dispatch rules for the same calling syntax. EG:
>>>>
>>>>
>>>>     public protocol P {
>>>>
>>>>         func mP() -> String
>>>>
>>>>      }
>>>>
>>>>     extension P {
>>>>
>>>>         func mP() -> String { return "P.mP" }
>>>>
>>>>         func mE() -> String { return "P.mE" }
>>>>
>>>>     }
>>>>
>>>>     struct S: P {
>>>>
>>>>         func mP() -> String { return "S.mP" }
>>>>
>>>>         func mE() -> String { return "S.mE" }
>>>>
>>>>     }
>>>>
>>>>     let s = S()
>>>>
>>>>     s.mP() // S.mP as expected
>>>>
>>>>     s.mE() // S.mE as expected
>>>>
>>>>     let p: P = s // Note: s now typed as P
>>>>
>>>>     p.mP() // S.mP as expected
>>>>
>>>>     p.mE() // P.mE unexpected!
>>>>
>>>>
>>>> Extension methods can also cause compatibility problems between
>>>> modules, consider:
>>>>
>>>>
>>>> In Module A
>>>>
>>>>     extension Int: P {
>>>>
>>>>         func m() -> String { print("A.m") }
>>>>
>>>>     }
>>>>
>>>>
>>>> In Module B
>>>>
>>>>     extension Int: P {
>>>>
>>>>         func m() -> String { print("B.m") }
>>>>
>>>>     }
>>>>
>>>>
>>>> In Module C
>>>>
>>>>     import A
>>>>
>>>>     import B // Should this be an error
>>>>
>>>>     let i = 0
>>>>
>>>>     i.m() // Should it return A.m or B.m?
>>>>
>>>>
>>>> This proposal cures the above two problems by separating extension
>>>> methods into two seperate use cases: implementations for methods and adding
>>>> methods and protocols post-hoc.
>>>>
>>>>
>>>> ## Implementing methods
>>>>
>>>>
>>>> If the extension is in the same file as the protocol/struct/class
>>>> declaration then it implements the methods and is dispatched using a
>>>> Vtable. EG:
>>>>
>>>>
>>>> File P.swift
>>>>
>>>>     protocol/struct/class P {
>>>>
>>>>         // func m() not declared in type since it is added by the
>>>> extension, under this proposal it is an error to include a declaration in a
>>>> type *and* in an extension
>>>>
>>>>     }
>>>>
>>>>     extension P {
>>>>
>>>>         func m() { print("P.m") } // m is added to the
>>>> protocol/struct/class declaration
>>>>
>>>>     }
>>>>
>>>>
>>>> Same or other file
>>>>
>>>>     struct S: P {
>>>>
>>>>         override func m() { print("S.m") } // Note override required
>>>> because m already has an implementation from the extension
>>>>
>>>>     }
>>>>
>>>>     let p: P = S() // Note typed as P
>>>>
>>>>     p.m() // Now prints S.m as expected
>>>>
>>>>
>>>> Extensions in the same file as the declaration can have any access, can
>>>> be final, and can have where clauses and provide inheritable
>>>> implementations.
>>>>
>>>>
>>>> In a protocol at present there is a difference in behaviour between a
>>>> protocol that declares a method that is then implemented in an extension
>>>> and a protocol that just has the method implemented in an extension and no
>>>> declaration. This situation only applies to protocols, for structs and
>>>> classes you cannot declare in type and implement in extensions. The
>>>> proposal unifies the behaviour of protocol/struct/class with extensions and
>>>> prevents the error of a minor typo between the protocol and extension
>>>> adding two methods instead of generating an error.
>>>>
>>>>
>>>> The implementation needed to achieve this is that a value instance
>>>> typed as a protocol is copied onto the heap, a pointer to its Vtable added,
>>>> and it is passed as a pointer. IE it becomes a class instance. No change
>>>> needed for a class instance typed as a protocol.
>>>>
>>>>
>>>> ## Post-hoc adding protocols and methods
>>>>
>>>>
>>>> A new type of extension is proposed, a "final extension", which can be
>>>> either in or outside the file in which the protocol/struct/class
>>>> declaration is in. EG:
>>>>
>>>>
>>>>     protocol P2 {
>>>>
>>>>         func m2P()
>>>>
>>>>     }
>>>>
>>>>     final extension S: P2 { // Note extension marked final
>>>>
>>>>         func m2P() { print("SP2.m2P") } // Implicitly final, completely
>>>> implements P2
>>>>
>>>>         func m2E() { print("SP2.m2E") } // Implicitly final, not an
>>>> existing method
>>>>
>>>>     }
>>>>
>>>>
>>>> Which are called as any other method would be called:
>>>>
>>>>
>>>>     let s = S()
>>>>
>>>>     s.m2P() // Prints SP2.m2P
>>>>
>>>>     s.m2E() // Prints SP2.m2E
>>>>
>>>>
>>>> A method added by a final extension is is implicitly final, as the name
>>>> would suggest, and cannot be overridden.
>>>>
>>>>
>>>> If the final extension:
>>>>
>>>>
>>>>   1. Adds a method, e.g. m2E, that method cannot already exist. IE a
>>>> final extension cannot override an existing method or implement a protocol
>>>> declared method that lacks an implementation unless it also post-hoc adds
>>>> the protocol.
>>>>
>>>>
>>>>   2. Adds a protocol then it must implement all the methods in that
>>>> protocol that are not currently implemented.
>>>>
>>>>
>>>>   3. Is outside of the file in which the protocol/struct/class
>>>> declaration is in then the extension and the methods can only have
>>>> fileprivate or internal access. This prevents post-hoc extensions from
>>>> numerous modules clashing, since they are not exported outside of the
>>>> module.
>>>>
>>>>
>>>> ## Possible future work (not part of this proposal)
>>>>
>>>>
>>>> This proposal will naturally allow bodies to be added to protocols
>>>> directly rather than via an extension, since under the proposal the
>>>> extension adds the declaration to the type so it is a small step to allow
>>>> the protocol methods to have an implementation.
>>>>
>>>>
>>>> In an opposite sense to the above adding bodies to protocols,
>>>> extensions could be allowed to add method declarations without bodies to
>>>> protocols.
>>>>
>>>>
>>>> The two above future work proposals, if both added, would add symmetry
>>>> to where declarations and bodies may appear for protocols.
>>>>
>>>>
>>>> ## In summary.
>>>>
>>>>
>>>> The proposal formalises the split use of extensions into their two
>>>> uses: implementing methods and post-hoc adding protocols and methods.
>>>> Syntax is added that clarifies the two use cases, the former are termed
>>>> extensions and must be in the same file as the type is declared, and the
>>>> latter are termed final extensions and can be in any file, however if they
>>>> are not in the type's file the they can only have fileprivate or internal
>>>> access.
>>>>
>>>>
>>>> Note the distinction between an extension in the same file and in a
>>>> separate file is consistent with the philosophy that there is special
>>>> status to the same file as proposed for private in
>>>> https://github.com/apple/swift-evolution/blob/master/proposa
>>>> ls/0169-improve-interaction-between-private-declarations-
>>>> and-extensions.md.
>>>>
>>>>
>>>> ====================================
>>>>
>>>> On 14 Apr 2017, at 8:17 am, Brent Royal-Gordon <brent at architechies.com>
>>>> wrote:
>>>>
>>>> On Apr 13, 2017, at 3:10 PM, Howard Lovatt via swift-evolution <
>>>> swift-evolution at swift.org> wrote:
>>>>
>>>>
>>>> I don't see that retroactive conformance needs to be exportable. If it
>>>> is exported then you cannot prevent clashes from two modules, this is a
>>>> known problem in C#. Because of this and other problems with C# extensions,
>>>> this style of extension were rejected by other language communities
>>>> (notably Java and Scala).
>>>>
>>>>
>>>> A better alternative for export is a new class that encapsulates the
>>>> standard type but with added methods for the protocol to be added. This way
>>>> there is no clash between modules. EG:
>>>>
>>>>
>>>>    public protocol P {
>>>>
>>>>        func m() -> String
>>>>
>>>>    }
>>>>
>>>>    public class PInt: P {
>>>>
>>>>        var value = 0
>>>>
>>>>        func m() -> String { return "PI.m" }
>>>>
>>>>    }
>>>>
>>>>
>>>> Howard, this would be very source-breaking and would fail to achieve
>>>> fundamental goals of Swift's protocol design. Removing retroactive
>>>> conformance is no more realistic than removing Objective-C bridging—another
>>>> feature which introduces various ugly edge cases and tricky behaviors but
>>>> is also non-negotiable.
>>>>
>>>> --
>>>> Brent Royal-Gordon
>>>> Architechies
>>>>
>>>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170419/f041d0bf/attachment.html>


More information about the swift-evolution mailing list