[swift-evolution] [Manifesto] Completing Generics

Thorsten Seitz tseitz42 at icloud.com
Fri Mar 4 00:15:45 CST 2016


If I understand you right you are describing multiple dynamic dispatch as it was used e.g. in the Cecil language or Dylan and I think Clojure has it, too.

That would certainly be a nice and powerful addition to Swift but we would have to think carefully how to mesh this with single dispatch from OOP and POP.
Maybe we could introduce global multimethods.

-Thorsten 


Am 04.03.2016 um 05:48 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org>:

>>> The choice of Equatable as an example for opening existentials is an
>>> interesting one here, because it's one of the few cases I can think of
>>> where differing dynamic types is actually fully defined: they're not
>>> equal. In ObjC we express that by starting every -isEqual:
>>> implementation with if (![other isKindOfClass:[self class]]) { return
>>> NO; }, which while clunky and easy to forget, does neatly express the
>>> desired semantics with no burden at the callsite.
>> 
>> It's a reasonable default, but it's not necessarily the right
>> implementation for every type.  One could imagine Polygons comparing
>> equal to Squares, for example.
> 
> If the "open" operation is sophisticated enough, when it's faced with mismatched concrete types but they both conform to a protocol with a existential that meets the requirements, it could return that existential.
> 
> That would mean that, in this scenario:
> 
>    protocol Shape: Equatable { ...}
>    
>    protocol Polygonal: Shape {
>        var vertices: [Vertex]
>    }
>    struct Square: Polygonal { ... }
>    struct Polygon: Polygonal { ... }
> 
> You could add this:
> 
>    extension Any<Polygonal>: Equatable {}
>    
>    func == (lhs: Any<Polygonal>, rhs: Any<Polygonal>) -> Bool {
>        for (lhsVertex, rhsVertex) in zip(lhs.vertices, rhs.vertices) {
>            if lhsVertex != rhsVertex {
>                return false
>            }
>        }
>        return true
>    }
> 
> And then, if `==(_: Any<Equatable>, _: Any<Equatable>)` were passed a Square and a Polygon, opening the `Any<Equatable>`s would give you a pair of `Any<Polygonal>`s.
> 
> This is obviously more complex than simply opening the existential, matching its concrete type against a requirement, and extracting the original value if it matches—it's looking at the concrete types of N existentials, simultaneously matching them *all* against a requirement to find a more specific type they can all be cast to, and then performing that cast. To do its job, it would need to see all of the operands at the same time and evaluate them together to find a type that would fit all of them.
> 
> That's what I was trying to get at when I wrote this example earlier in the thread:
> 
>    func == (e1: Any<Equatable>, e2: Any<Equatable>) -> Bool {
>        guard let concreteE1<T: Equatable> = e1 as? T, concreteE2 = e2 as? T else {
>            return false
>        }
>        
>        return concreteE1 == concreteE2
>    }
> 
> If both parameters were `Square`s, then `T` would be a `Square`. But if one was a `Square` and the other a `Polygon`, `T` could be an `Any<Polygonal>`. Because you are simultaneously matching both values, you don't have to try the match both ways, and the operation is free to return a more specific protocol existential if that's the best it can do.
> 
> (And if there was no `Equatable` type they could be cast to that was more specific than `Any<Equatable>`, `T` would at least notionally be the bottom type and, since neither existential contains a value of the bottom type, both `as?` casts would return `nil`. `guard let` would then see those `nil`s and send you down the `else` branch.)
> 
> -- 
> Brent Royal-Gordon
> Architechies
> 
> _______________________________________________
> 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