[swift-evolution] [Proposal] Associated Type and Generic One-to-One Mapping

Xiaodi Wu xiaodi.wu at gmail.com
Sat Jun 24 20:41:26 CDT 2017


On Fri, Jun 23, 2017 at 8:29 PM, David Moore via swift-evolution <
swift-evolution at swift.org> wrote:

>
> protocol Bondable {
>
>     associatedtype Key
>
>     associatedtype Value
>
>
>
>     static func new() -> Self
>
>
>
>     func value(forKey key: Key) -> Value?
>
>
>
>     mutating func updateValue(_ value: Value?, forKey key: Key)
>
> }
>
>
> extension Dictionary: Bondable {
>
>     static func new() -> Dictionary<Key, Value> {
>
>         return Dictionary<Key, Value>()
>
>     }
>
>
>
>     func value(forKey key: Key) -> Value? {
>
>         return self[key]
>
>     }
>
>
>
>     mutating func updateValue(_ value: Value?, forKey key: Key) {
>
>         self[key] = value
>
>     }
>
> }
>
>
> struct Bridge<A, B> {
>
>     struct Getter {
>
>         var transform: ((B) -> A?)
>
>     }
>
>
>
>     struct Setter {
>
>         var transform: ((A) -> B?)
>
>     }
>
>
>
>     var get: Getter
>
>     var set: Setter
>
> }
>
>
> protocol Bonding {
>
>     associatedtype Dictionary: Bondable
>
>     associatedtype Value
>
>
>
>     var key: Dictionary.Key { get set }
>
>
>
>     var bridge: Bridge<Value, Dictionary.Value> { get set }
>
>
>
>     func value(from aDictionary: Dictionary, using set: Bridge<Value,
> Dictionary.Value>.Getter) -> Value?
>
>
>
>     func addValue(_ value: Value, to aDictionary: inout Dictionary, using
> get: Bridge<Value, Dictionary.Value>.Setter)
>
> }
>
>
> extension Bonding {
>
>
>
>     func value(from aDictionary: Dictionary, using get: Bridge<Value,
> Dictionary.Value>.Getter) -> Value? {
>
>         if let value = aDictionary.value(forKey: key) {
>
>             return get.transform(value)
>
>         } else {
>
>             return nil
>
>
>
>         }
>
>     }
>
>
>
>     func addValue(_ value: Value, to aDictionary: inout Dictionary, using
> set: Bridge<Value, Dictionary.Value>.Setter) {
>
>         aDictionary.updateValue(set.transform(value), forKey: key)
>
>     }
>
> }
>
>
> struct ComplexBond<__Dictionary: Bondable, __Value>: Bonding {
>
>     typealias Dictionary = __Dictionary
>
>     typealias Value = __Value
>
>
>
>     var key: __Dictionary.Key
>
>
>
>     var bridge: Bridge<__Value, __Dictionary.Value>
>
> }
>
> The above is an example implementation of the `Bonding` protocol, where
> the preexisting names of the associated types, `Dictionary` and `Value`,
> are already appropriately named. It would be great if I could just add a
> simple keyword prefixing the `typealias` keyword which would enable the
> inferencing behavior.
>

You already don't need to use a typealias for `Value`. It can already be
inferred from the type of `bridge`.

```
struct ComplexBond<__Dictionary: Bondable, Value>: Bonding {
  typealias Dictionary = __Dictionary
  var key: Dictionary.Key
  var bridge: Bridge<Value, Dictionary.Value>
}

// This compiles.
```

The issue with `Dictionary` and `__Dictionary` is interesting, and here is
my analysis:

The thing is, you don't actually need `Dictionary` to be a typealias for
`__Dictionary` _in order for `ComplexBond` to conform to `Bonding`_ (though
you may require this for the semantics of `ComplexBond` itself). For
instance, I can instead declare a `ComplexBond2` as follows:

```
struct ComplexBond2<__Dictionary: Bondable, Value, __AnotherDictionary:
Bondable>: Bonding
where __Dictionary.Key == __AnotherDictionary.Key, __Dictionary.Value ==
__AnotherDictionary.Value {
  typealias Dictionary = __AnotherDictionary
  var key: __Dictionary.Key
  var bridge: Bridge<Value, __Dictionary.Value>
}
```

Put another way, for the purposes of conformance, it only matters that
`__Dictionary.Key` is the same type as `Dictionary.Key` and that
`__Dictionary.Value` is the same type as `Dictionary.Value`, but
`ComplexBond.__Dictionary` and `Bonding.Dictionary` are not required _by
the protocol_ to be the same type. Of course, if `ComplexBond` had to
implement some protocol requirement by which you could infer that
`__Dictionary` must be the same type as `Dictionary`, then that inference
would be possible too without a typealias.

Put another way, the fact that `typealias Dictionary = __Dictionary` is
required here is not merely a reflection of some shortcoming in the
expressiveness of the language: it reflects the fact that this relationship
cannot be inferred because the relationship doesn't have to be that way--as
far as conformance of `ComplexBond` to `Bonding` is concerned. Instead,
with that statement, you're making a _choice_ based on the desired
semantics for `ComplexBond`, a choice that is not forced upon you by the
protocol to which it conforms. In that sense, it is actually useful that
Swift prevents you from naming both `Dictionary` and `__Dictionary` the
same thing: you are reminded that the relationship between the two is a
choice of the conforming type, not an inevitability of protocol
conformance; by contrast, if some requirement *does* make the relationship
an inevitability, such a typealias is not required and you *can* name the
two with the same name (as is the case with `Value`).



> On Jun 23, 2017, 8:36 PM -0400, Xiaodi Wu <xiaodi.wu at gmail.com>, wrote:
>
> Yes, examples will be helpful.
>
>
> On Fri, Jun 23, 2017 at 19:28 David Moore via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> I do indeed have quite a few real examples of this, such prompted me to
>> bring this up. I think this could be done without any impact to existing
>> code, but it would require some type of keyword. Take the following as a
>> possible prototype.
>>
>> protocol Foo {
>>     associatedtype T
>> }
>>
>> struct Bar<T> : Foo {
>>     keyword typealias T // Or really any other syntactical implementation.
>> }
>>
>> With an opt-in method we could implement this without affecting existing
>> code, thereby making this more viable. I will send some examples later.
>>
>> On Jun 23, 2017, at 6:52 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>
>> There could be source-breaking implications for such a feature,
>> especially with retroactive conformance. Therefore, I think this could be
>> very tricky and I'd want to be convinced that the benefits are very great
>> to risk such a disturbance. Here, I think the problem is rather mild, and
>> here's why:
>>
>> It is true that, in your example specifically, renaming T to U is the
>> only solution (that I know of, anyway). However, for any "serious" protocol
>> P, there's likely to be a required property of type P.T, or a function that
>> takes an argument of type P.T or returns a value of type P.T. Therefore,
>> implementing that requirement in Bar with a corresponding
>> property/argument/return value of type Bar.T would generally do the trick.
>>
>> Have you got any real-world examples where you're running into this issue?
>>
>> On Fri, Jun 23, 2017 at 17:03 David Moore via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> Hello Swift Evolution,
>>>
>>> This may have already been discussed before, but I just came across a
>>> bothersome language aspect which reminded me to propose a solution.
>>> Currently, if we want to add generics to a protocol the only way to do so
>>> is with associated types. I am quite fine with the current approach with
>>> respect to those semantics.
>>>
>>> There is, however, a weakness that is built in with using associated
>>> types. That weakness is the lack of associated type and generic inference.
>>> To be more clear about what I mean, take the following as an example.
>>>
>>> protocol Foo {
>>>     associatedtype T
>>> }
>>>
>>> The foregoing protocol is quite basic, but uses an associated type with
>>> the name “T.” Giving the associated type that name will illustrate the
>>> dilemma encountered later on down the pipeline.
>>>
>>> struct Bar<T> : Foo {
>>>     // What am I supposed to do? The name is used for both the generic
>>> and the type alias Foo needs for conformance.
>>>     typealias T = T // Error!
>>> }
>>>
>>> The above illustrates a situation where we want to connect the generic,
>>> which is supposedly named appropriately, and the protocol’s associated
>>> type. There is no elegant solution for this at the moment. All I could do
>>> is the following.
>>>
>>> struct Bar<U> : Foo {
>>>     typealias T = U // Not nearly as readable.
>>> }
>>>
>>> Now, there may be a few ways to go about adding support for generic
>>> inference. The compiler as it is already does some awesome inference get
>>> when it comes to generics, so why not take it a step further? I propose the
>>> introduction of a keyword, or anything else that could work, to specify
>>> explicitly what a given type alias declaration would do when it comes to
>>> inferencing. Requiring a keyword would ensure source compatibility remains
>>> intact, and it would also make the code more readable.
>>>
>>> I don’t know if this would be something that people could find useful,
>>> but I surely would. The implicit mapping of an associated type and a given
>>> generic by their names, would be a natural development.
>>>
>>> Let me know if this is just useless, or if could be a potential feature.
>>>
>>> Thank you,
>>> David Moore
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>
> _______________________________________________
> 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/20170624/b6973d46/attachment.html>


More information about the swift-evolution mailing list