[swift-evolution] [Idea] Type erasure for protocols with Self or associated type requirements.

John McCall rjmccall at apple.com
Sun Jan 17 22:31:42 CST 2016


> On Jan 11, 2016, at 10:44 PM, Vatsal Manot via swift-evolution <swift-evolution at swift.org> wrote:
> (This is my first time on swift-evolution / Mailman, I apologize for any formatting errors)
> 
> As we know, the following fails:
>> protocol _Protocol
> {
>     static var _Parameter: Any.Type { get }
>     
>     var nonGenericVariable: Int { get }
>     var _parameter: Any { get set }
>     
>     func _inputParameter(_: Any) -> Any?
> }
>> Protocol.self
>> with the error:
>> protocol 'Protocol' can only be used as a generic constraint because it has Self or associated type requirements
>> For quite some time, I have been using a particular workaround for this:
>> protocol _Protocol
> {
>     static var _Parameter: Any.Type { get }
>     
>     var nonGenericVariable: Int { get }
>     var _parameter: Any { get set }
>     
>     func _inputParameter(_: Any) -> Any?
> }
>> extension _Protocol where Self: Protocol
> {
>     static var _Parameter: Any.Type
>     {
>         return Parameter.self
>     }
>     
>     var _parameter: Any
>     {
>         get
>         {
>             return parameter
>         }
>         
>         set
>         {
>             parameter = newValue as! Parameter
>         }
>     }
>     
>     func _inputParameter(parameter: Any) -> Any?
>     {
>         return (parameter as? Parameter).map(inputParameter)
>     }
> }
>> protocol Protocol: _Protocol
> {
>     typealias Parameter
>     
>     var nonGenericVariable: Int { get }
>     var parameter: Parameter { get set }
>     
>     func inputParameter(_: Parameter) -> Parameter
> }
>> And it has worked well so far. 
>> The idea is to let the compiler generate these type-erased ‘versions’ on demand, using syntax like:
> 
> Protocol.Opaque 
>> The rough procedure of creating a type-erased version for a given protocol is as follows:
> 
> - Create a protocol with an underscore prefixed to the name of the target protocol.
> 
> - Expose all the non-ATD (self or associated type dependent) constructs as requirements (essentially just copying the declarations) 
> 
> - Expose all the ATD constructs as requirements with associated types replaced with “Any”, with an underscore prefixed to their name changing the return value of functions accepting ATD inputs to an optional version of the same type.  
> 
> - Extend the target protocol with implementations of these type-erased ‘versions’ of ATD constructs. For variables, a computed property is provided which returns the target variable, and force casts newValue in the setter (if any) to the required ATD type. For functions, the parameters undergo an attempted cast to the required ATD types, and are then mapped over the original implementation. Functions without ATD parameters can return just Any, because there is no casting involved. 
> 
> - Initializers become static functions with prefixed underscores, returning an Optional<Protocol.Opaque>
> 
> I admit that I don’t have a plan on how this should be implemented. My fear is that it require tremendous amounts of metadata, bloating the binary size, and so I have submitted this as an idea and not a proposal.

We’re already slowly progressing towards an implementation design which will allow us to simply lift some of these restrictions.  The work will be done in the Swift 3 time-frame; whether we actually lift the restrictions on existential types significantly in this release is a lot less certain, but there’s not much point in having the discussion right now, I think.

John.

> 
> I have created a gist (https://gist.github.com/vmanot/888afc3f26caf142cd21 <https://gist.github.com/vmanot/888afc3f26caf142cd21>) as a demonstration. 
> 
> This pattern has helped on innumerable occasions, and is used in my projects to allow some runtime tricks. It also helps me forward implementations in a way similar (behavior-wise) to “AnyGenerator” 
> 
> import Swift
> 
> struct AnyGenerator<Element>
> {
>     var base: _GeneratorType
>     
>     init<G: protocol<_GeneratorType, GeneratorType> where G.Element == Element>(base: G)
>     {
>         self.base = base
>     }
>     
>     mutating func next() -> Element?
>     {
>         return (base._next() as! Optional<Element>)
>     }
> }
> 
> AnyGenerator(base: [1, 2, 3].generate())
> 
> (I have tested this with an actual_GeneratorType implementation, and it works)
> 
> Please let me know what you think of this idea, and whether it is viable or not.
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160117/f5577ff9/attachment.html>


More information about the swift-evolution mailing list