[swift-evolution] [Pitch] Generalized supertype constraints
Magnus Ahltorp
map at kth.se
Mon Dec 11 10:25:32 CST 2017
Mutation is more or less modeled as reassignment in Swift, there is no difference between a variable that can be mutated and one that can be reassigned (right?). There are however differences between inout's real behaviour and what some might expect; some might expect that it is modified at the same time (and as many times) as if the function was inlined.
struct F {
var a : Int {
didSet {
print("set \(a)")
}
}
}
func setTimes(a: inout Int, times: Int) {
for _ in 1...times {
a += 1
}
}
var foo = F(a: 0)
// prints set 1 … set 10
for _ in 1...10 {
foo.a += 1
}
setTimes(a: &foo.a, times: 10) // only prints set 20
However, your generalization is already possible, just change the function head to:
func foo<E:P>(_ p: inout E)
which, if we add a member to the protocol and something meaningful for the function to do becomes something like this:
protocol P { var a : Int {get set} }
func foo<E:P>(_ p: inout E) {
p.a += 1
}
struct F : P { var a : Int }
var f = F(a: 0)
foo(&f)
print(f)
Was there some other problem you were trying to solve that can't be solved by doing this?
/Magnus
> 11 Dec. 2017 03:49 Adrian Zubarev via swift-evolution <swift-evolution at swift.org> wrote:
>
> I see, that makes totally sense. I thought about `inout` only in terms of mutation not reassignment from within the scope itself.
>
>
> Am 10. Dezember 2017 um 17:56:56, Jonathan Keller (jkeller234 at icloud.com) schrieb:
>
>> No, inout should *not* be covariant, since foo can assign any subtype of P to its parameter, not just F:
>>
>> protocol P {}
>> struct F: P {}
>> struct G: P {}
>>
>> func foo(_ p: inout P) {
>> p = G()
>> }
>>
>> var f = F()
>> foo(&f) // assigns a value of type G to a variable of type F
>>
>> From Jonathan
>>
>> On Dec 10, 2017, at 12:51 AM, Adrian Zubarev via swift-evolution <swift-evolution at swift.org> wrote:
>>
>>> Hello Matthew, I have more more question about the generalized supertype constraints. Does the generalization also covers inout? I found a really annoying case with inout.
>>>
>>> protocol P {}
>>>
>>> func foo(_ p: inout P) {
>>> print(p)
>>> }
>>>
>>> struct F : P {}
>>>
>>> var f = F()
>>>
>>> foo(&f) // won't compile until we explicitly write `var f: P = F()`
>>>
>>> Shouldn’t we allow inout to have subtypes as well, like inout F : inout P?
>>>
>>> This is actually one of the pain points with the generic version of the print function. We cannot pass an arbitrary TextOutputStream to it without sacrificing the type. I doubt the generic print function is justified, because TextOuputStream does not have associated types nor a Self constraint. Furthermore it forces you to create a custom type-erased wrapper that can hold an arbitrary TextOutputSteram.
>>>
>>> If this is part of a totally different topic, I’ll move it in it’s own thread.
>>>
>>>
>>>
>>> Am 2. Dezember 2017 um 19:03:24, Matthew Johnson via swift-evolution (swift-evolution at swift.org) schrieb:
>>>
>>>> This thread received very light, but positive feedback. I would really like to see this feature added and am willing to draft and official proposal but am not able to implement it. If anyone is interested in collaborating just let me know.
>>>>
>>>>
>>>>> On Nov 24, 2017, at 5:03 PM, Matthew Johnson via swift-evolution <swift-evolution at swift.org> wrote:
>>>>>
>>>>> One of the most frequent frustrations I encounter when writing generic code in Swift is the requirement that supertype constraints be concrete. When I mentioned this on Twitter (https://twitter.com/anandabits/status/929958479598534656) Doug Gregor mentioned that this feature is smaller and mostly straightforward to design and implement (https://twitter.com/dgregor79/status/929975472779288576).
>>>>>
>>>>> I currently have a PR open to add the high-level description of this feature found below to the generics manifesto (https://github.com/apple/swift/pull/13012):
>>>>>
>>>>> Currently, supertype constraints may only be specified using a concrete class or protocol type. This prevents us from abstracting over the supertype.
>>>>>
>>>>> ```swift
>>>>> protocol P {
>>>>> associatedtype Base
>>>>> associatedtype Derived: Base
>>>>> }
>>>>> ```
>>>>>
>>>>> In the above example `Base` may be any type. `Derived` may be the same as `Base` or may be _any_ subtype of `Base`. All subtype relationships supported by Swift should be supported in this context including, but not limited to, classes and subclasses, existentials and conforming concrete types or refining existentials, `T?` and `T`, `((Base) -> Void)` and `((Derived) -> Void)`, etc.
>>>>>
>>>>> Generalized supertype constraints would be accepted in all syntactic locations where generic constraints are accepted.
>>>>>
>>>>> I would like to see generalized supertype constraints make it into Swift 5 if possible. I am not an implementer so I will not be able to bring a proposal forward alone but am interested in collaborating with anyone interested in working on implementation.
>>>>>
>>>>> I am also interested in hearing general feedback on this feature from the community at large. Have you also found this limitation frustrating? In what contexts? Does anyone have reservations about introducing this capability? If so, what are they?
>>>>>
>>>>> Matthew
>>>>>
>>>>> _______________________________________________
>>>>> 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
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
More information about the swift-evolution
mailing list