[swift-evolution] [Idea] Type erasure for protocols with Self or associated type requirements.
Howard Lovatt
howard.lovatt at gmail.com
Fri Jan 15 20:17:57 CST 2016
@Vatsai,
Not sure I get what you mean. Can you expand the example?
-- Howard.
PS Re. Mailman: I always make sure a CC swift-evolution at swift.org
> On 12 Jan 2016, at 10:47 PM, Vatsal Manot <vatsal.manot at yahoo.com> wrote:
>
> Sure, covariance might solve some cases, but what if you had:
>
> struct X<T where T: SomeProtocol>
> {
>
> }
>
> Coercion to X<Any> would be impossible. What I’m aiming for is an opaque interface to any given protocol.
>
> (On an unrelated note, if I want this reply to reflected in the archive, do I just /cc swift-evolution? A little new to Mailman, sorry.)
>
>> On 12-Jan-2016, at 3:29 PM, Howard Lovatt <howard.lovatt at gmail.com> wrote:
>>
>> I have proposed something very similar:
>>
>> [swift-evolution] Make generics covariant and add generics to protocols
>>
>> Although I didn’t mention this in the proposal I am aware that if that proposal was accepted then Self could become a shorthand for the type name and loose all its ‘magic’ properties and would be in effect what you are suggesting.
>>
>>
>>> On 12 Jan 2016, at 5: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.
>>>
>>> I have created a gist (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/20160116/75b99b1e/attachment.html>
More information about the swift-evolution
mailing list