I think an important point is that `all<>` should NOT be restricted to having 
only one reference or value type!
This is a little problematic and I’m not a compiler expert so from my 
perspective I could allow that but in a different way you see it (I’ll explain 
below).

Ceylon does not have any restrictions like that. You can form the type 
intersection of String and Integer for example, which are both classes in 
Ceylon and because Ceylon like Swift only knows single inheritance between 
classes, the result is simply `Nothing` (the bottom type which has no 
elements). So there is no need to forbid this explicitly, because the types 
work out just fine.
If I remember correctly someone said that `Ceylon` does use `all` and `any` for 
its Optional?! We can’t to do this in Swift as far as I know our playground.

We don’t have `Nothing` in Swift. The counterpart would be `Optional` but such 
a type is made explicit by `?` symbol like `Type? == Optional<Type>` and the 
only equivalent to `Nothing` would be `nil`.

That been said, if `All<>` would always intersect (what I haven’t proposed here 
at all) we can’t replace `protocol<>` with `All<>` because if we look at two 
distinct protocols `A` and `B` and try to merge them into a type `All<A, B>` 
would resolve in `implicit nil` where the old fashion way is not `protocol<A, 
B>`. By the way, really an `implicit nil`? This is not a great idea if you ask 
me. 

You may have a look at this document: 
https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md

`All<String, Int> == implicit nil` (your way) while it probably `All<String, 
Int>? == nil` would serve your wished behavior, but again I wasn’t proposing on 
solving this issue here. I’d like to solve this problem 
https://openradar.appspot.com/20990743 and open the door for `AnyStruct`, 
`AnyEnum`, `AnyValue` (maybe `AnyTuple` someday). This is what your hint for 
`Any<>` combined with my `All<>` might create.

If we allow multiple reference and value types for `All<>` this will only work 
with subtypeable types, because as I already described above `All<StructA, 
StructB>` can’t be merged.

A scenario that will work might look like this:

class A {}     class B: A {}     class C: B {}

`All<B, C, A>` from the given types and my understanding the compile could and 
should infer `C` from here (not `B` compared to your example below).

To sum up a little we would have these facts:

- for subtypeable types the compile will search the highest type from the 
inheritance path and ignore all other lower base types (for `All<A, B>` the 
compile would infer `B == All<B> == All<B, A>`)

- the oder of types should not matter because of the previous fact

That been said do we still need the whole inheritance branch inside of `All<>`? 
I don’t think so.

Furthermore if we definitely should ignore order of `Types` inside the angle 
brackets like `All<A, B> == All<B, A>`, because it makes sense from the context 
of creating a `Type` that is constrained to `A` AND `B` where AND is 
commutative. I didn’t thought of at first glance, but thanks to your examples 
it’s clear to me know.

———

If one would want to store or pass `A` from `class A: ClassB, ProtocolC` in 
some generic context and we don’t allow a second reference for `All<>` the 
following sample won’t work (generalized `class` is assumed):

`func <T: class>(value: All<T, ClassB, ProtocolC>)` but you can workaround here 
`func <T: ClassB>(value: All<T, ProtocolC>)` which can already be done `func 
<T: ClassB where T: ProtocolC>(value: T)`

The only problem that rises up here is that we can’t store that value inside a 
non-generic `Type` with both distinct `ClassB` and `ProtocolC` constraints 
merged together. As you might guess `ProtocolC` is applied onto `A` but not on 
`B` where `All<ClassB, ProtocolC>` will create a whole new type for us 
(https://openradar.appspot.com/20990743).

Furthermore you can form type intersections between reference types which 
inherit from each other. The resulting intersection type is valid and is just 
equal to the more specific type. And of course you can form type intersections 
of a reference or value type with itself (i.e. all<SomeClass, SomeClass>).
Why should that be useful you may ask?

This generality is important when using intersection types with generics: let’s 
consider the type of a function forming the intersection of two sets with 
different element types:

func union<T, U>(a: Set<T>, b: Set<U>) -> Set<all<T, U>> { … }


Requiring all<T,U> to have at most one reference or value type (which must be 
at first position) would impose some unnecessary restrictions:

Given the following:

protocol Named {}
class Person : Named {}
class Employee : Person {}

let people: Set<Person>
let otherPeople: Set<Person>
let employees: Set<Employee>
let namedOnes: Set<Named>

// unnecessary restriction:
let x1 = union(namedOnes, people)  // not allowed, because result type contains 
all<Named, Person> 
// the restriction would require us to write:
let x2 = union(people, namedOnes)  // ok, result type would be Set<all<Person, 
Named>> which would be simplified by the compiler to Set<Named> (Ceylon does 
this!)
// unnecessary restriction:
let x3 = union(people, employees)   // not allowed, because result type would 
contain all<Person, Employee> with two reference types
// unnecessary restriction
let x4 = union(people, otherPeople)   // not allowed, because result type 
contains all<Person, Person> with two reference types

IMO these should all be allowed (and are possible in Ceylon). 
The result type of x1 would be Set<all<Named, Person>> which would be 
simplified by the compiler to Set<Named>.
The result type of x2 would be Set<all<Person, Named>> which would be 
simplified by the compiler to Set<Named>.
The result type of x3 would be Set<all<Person, Employee>> which would be 
simplified by the compiler to Set<Employee>.
The result type of x4 would be Set<all<Person, Person>> which would be 
simplified by the compiler to Set<Person>.

-Thorsten

Pleas rethink your example here, `Intersection<>` might be a proposal of its 
own (there are some problems with `Nothing` I described above).

——— 

`Any<>` will pick only one type from its angle brackets. The types must be 
distinct to each other, but `Any<>` is a whole other proposal which has its own 
problems like:

protocol A {}    protocol B {}     class C: A, B {}

func foo(value: Any<A, B>) {
     // if else if won’t handle `value` correctly if it’s `C`
}

Is such a thing intended? I guess `Any<>` should just proceed when the first 
match is found at compile time and it’s up to the developer to handle `value` 
correctly.

In different thread I just answered my vision for `All<>` and future `Any<>`:

There are two future directions in my proposal: 
       (1) `Any<>` which takes only one type from the angle brackets: 
`Any<String, Int>` or `String | Int`
       (2) if we already have a generalized `class` keyword, so why we don’t 
get `struct` and `enum` as well?

With this we can create a typealias for `AnyValue` like this (at least for 
generalized extendable types): 
       `typealias AnyStruct = All<struct>`
       `typealias AnyEnum = All<enum>`
       `typealias AnyValue = Any<All<struct>, All<enum>>`
       `typealias AnyValue = AnyStruct | AnyEnum`

-- 
Adrian Zubarev
Sent with Airmail
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to