[swift-evolution-announce] [Review] SE-0089: Replace protocol<P1, P2> syntax with Any<P1, P2>

Ok, that’s indeed a type error and I think that I now see why Any<Collection where .Element == Int> does not conform to T: Collection where T.Element == Int. This is because T stands for one *fixed* type out of the set of types described by Any<Collection where .Element == Int> whereas Any<Collection where .Element == Int> stands for *all* of them, which is more, so it cannot conform to T.

So doSomething(someSeq, anotherSeq) would be a type error and I’m fine with that.

We could try to write

func doSomething<T: Any<Collection where T.Element == Int>>(x: T, y: T) { … }

but then we wouldn’t be able to write the body as desired, because x.Index would be a different type from y.Index (and rightly so).

I think all this is fine, though. The above method doSomething() can only be written with runtime casts of the indices by opening them explicitly with one of the proposed syntaxes.

-Thorsten

···

Am 08.06.2016 um 23:37 schrieb Austin Zheng via swift-evolution <swift-evolution@swift.org>:

func doSomething<T : Collection where T.Element == Int>(x: T, y: T) {
  // Get indexes out of x and use them to index into y
  var idx = x.startIndex
  while (idx != x.endIndex || idx != y.endIndex) {
    print(x[idx])
    print(y[idx])
    idx = x.nextIndex(idx)
  }
}
let someSeq : Any<Collection where .Element == Int> = // ...
let anotherSeq : Any<Collection where .Element == Int> = // ...
// Trouble!
// someSeq and anotherSeq are the same existential type
// But the concrete index types within each of the existential variables may be different
doSomething(someSeq, anotherSeq)

It's this situation (using an existential type to fulfill a generic type parameter constrained to the same requirements that comprise that existential) that requires either of the two options that Dave presented, due to our lack of compile-time type information about the fulfilling type's associated types.

FWIW my opinion is that existentials either shouldn't be allowed to stand in for generic type parameters, or Dave's option #1 if they are.

The implied promise of a generic type parameter T right now is that T always stands for the same concrete type (modulo things like passing in a subclass where a class would do), and likewise for all of T's associated types (T.Foo is always the same type everywhere in the context where T is valid). This is what makes using anything with 'self' requirements in a generic context sound. Allowing existentials to satisfy T would violate that constraint.

Relaxing these semantics would make it too easy to write code that traps at runtime "without the user having to reach" (to paraphrase Jordan from the "Swift philosophy" thread). Anyone who really really wants to write code that is 'compile-time unsound' in this way should have to explicitly type erase using concrete wrappers.

Yes, exactly.

Yup.

-Thorsten

···

Am 09.06.2016 um 00:13 schrieb Matthew Johnson via swift-evolution <swift-evolution@swift.org>:

On Jun 8, 2016, at 4:47 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

Best,
Austin

On Wed, Jun 8, 2016 at 2:37 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:
We might be talking past each other. I think Matthew is talking about using an existential outside the context of generic functions. For example, something like this should be trap-proof (as long as 'x' is immutable, which it is in this example):

func copySequenceIntoArray(x: Any<Sequence where .Iterator.Element == Int>) -> [Int] {
  var buffer : [Int] =
        // Stupid implementation to make a point
  var iterator : x.Iterator = x.makeIterator()
  while true {
    let nextItem : Int? = iterator.next()
    if let nextItem = nextItem {
      buffer.append(nextItem)
    } else {
      return buffer
    }
  }
}

Even this would never trap as well:

func copySequenceIntoArray<T>(x: Any<Sequence where .Iterator.Element == T>) -> [T] {
  var buffer : [T] =
  for item in x {
    buffer.append(item)
  }
  return buffer
}

Where we run into difficulty is something like this (forgive my abuse of the collections API; I don't remember all the new indexing APIs off the top of my head):

func doSomething<T : Collection where T.Element == Int>(x: T, y: T) {
  // Get indexes out of x and use them to index into y
  var idx = x.startIndex
  while (idx != x.endIndex || idx != y.endIndex) {
    print(x[idx])
    print(y[idx])
    idx = x.nextIndex(idx)
  }
}
let someSeq : Any<Collection where .Element == Int> = // ...
let anotherSeq : Any<Collection where .Element == Int> = // ...
// Trouble!
// someSeq and anotherSeq are the same existential type
// But the concrete index types within each of the existential variables may be different
doSomething(someSeq, anotherSeq)

It's this situation (using an existential type to fulfill a generic type parameter constrained to the same requirements that comprise that existential) that requires either of the two options that Dave presented, due to our lack of compile-time type information about the fulfilling type's associated types.

Best,
Austin

On Wed, Jun 8, 2016 at 2:33 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPad

> On Jun 8, 2016, at 3:16 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
>
>> on Wed Jun 08 2016, Thorsten Seitz <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>
>> Ah, thanks, I forgot! I still consider this a bug, though (will have
>> to read up again what the reasons are for that behavior).
>
> Yes, but in the case of the issue we're discussing, the choices are:
>
> 1. Omit from the existential's API any protocol requirements that depend
> on Self or associated types, in which case it *can't* conform to
> itself because it doesn't fulfill the requirements.

They don't need to be omitted. They are exposed in different ways depending on how the existential is constrained. Austin's proposal was originally written to omit some members but it was modified based on feedback from Doug Gregor IIRC (Austin, is that right?). Now it contains examples showing how these members are made available in a safe way. Some members may still not be usable because you can't form an argument but IIRC the suggestion was that they be exposed anyway for consistency.

>
> 2. Erase type relationships and trap at runtime when they don't line up.
>
> Matthew has been arguing against #2, but you can't “fix the bug” without
> it.
>
>>
>> -Thorsten
>>
>>> Am 08.06.2016 um 21:43 schrieb Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>>:
>>>
>>> It's not possible, even with Swift's current implementation of
>>> existentials. A protocol type P isn't considered to conform to
>>> itself, thus the following is rejected:
>>>
>>> let a : MyProtocol = // ...
>>> func myFunc<T : MyProtocol>(x: T) {
>>> // ....
>>> }
>>> myFunc(a) // "Cannot invoke 'myFunc' with an argument list of type MyProtocol"
>>>
>>> Changing how this works is probably worth a proposal by itself.
>>>
>>> Austin
>>>
>>>
>>> On Wed, Jun 8, 2016 at 12:34 PM, Thorsten Seitz via swift-evolution >> >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >> >>> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> >> >>> wrote:
>>>
>>>> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution
>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>>:
>>>>
>>>>
>>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:
>>>>
>>>>>> On Jun 7, 2016, at 9:15 PM, Dave Abrahams >> >>>>>> <dabrahams@apple.com <mailto:dabrahams@apple.com> >> >>>>>> <mailto:dabrahams@apple.com <mailto:dabrahams@apple.com>>> >> >>>>>> wrote:
>>>>>>
>>>>>>
>>>>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt; >> >>>>>> <http://matthew-at-anandabits.com/ >> >>>>>> <http://matthew-at-anandabits.com/&gt;&gt;&gt; wrote:
>>>>>
>>>>>>>> On Jun 7, 2016, at 4:13 PM, Dave Abrahams via swift-evolution >> >>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >> >>>>>>>> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> >> >>>>>>>> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> on Tue Jun 07 2016, Matthew Johnson >> >>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >> >>>>>>>> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> >> >>>>>>>> wrote:
>>>>>>>
>>>>>>>>>> , but haven't realized
>>>>>>>>>> that if you step around the type relationships encoded in Self
>>>>>>>>>> requirements and associated types you end up with types that appear to
>>>>>>>>>> interoperate but in fact trap at runtime unless used in exactly the
>>>>>>>>>> right way.
>>>>>>>>>
>>>>>>>>> Trap at runtime? How so? Generalized existentials should still be
>>>>>>>>> type-safe.
>>>>>>>>
>>>>>>>> There are two choices when you erase static type relationships:
>>>>>>>>
>>>>>>>> 1. Acheive type-safety by trapping at runtime
>>>>>>>>
>>>>>>>> FloatingPoint(3.0 as Float) + FloatingPoint(3.0 as Double) // trap
>>>>>>>>
>>>>>>>> 2. Don't expose protocol requirements that involve these relationships,
>>>>>>>> which would prevent the code above from compiling and prevent
>>>>>>>> FloatingPoint from conforming to itself.
>>>>>>>>
>>>>>>>>> Or are you talking about the hypothetical types / behaviors people
>>>>>>>>> think they want when they don’t fully understand what is happening...
>>>>>>>>
>>>>>>>> I don't know what you mean here. I think generalized existentials will
>>>>>>>> be nice to have, but I think most people will want them to do something
>>>>>>>> they can't possibly do.
>>>>>>>
>>>>>>> Exactly. What I meant is that people think they want that expression
>>>>>>> to compile because they don’t understand that the only thing it can do
>>>>>>> is trap. I said “hypothetical” because producing a compile time error
>>>>>>> rather than a runtime trap is the only sane thing to do. Your comment
>>>>>>> surprised me because I can’t imagine we would move forward in Swift
>>>>>>> with the approach of trapping.
>>>>>>
>>>>>> I would very much like to be able to create instances of “Collection
>>>>>> where Element == Int” so we can throw away the wrappers in the stdlib.
>>>>>> That will require some type mismatches to be caught at runtime via
>>>>>> trapping.
>>>>>
>>>>> For invalid index because the existential accepts a type erased index?
>>>>
>>>> Exactly.
>>>>
>>>>> How do you decide where to draw the line here? It feels like a very
>>>>> slippery slope for a language where safety is a stated priority to
>>>>> start adopting a strategy of runtime trapping for something as
>>>>> fundamental as how you expose members on an existential.
>>>>
>>>> If you don't do this, the alternative is that “Collection where Element
>>>> == Int” does not conform to Collection. That's weird and not very
>>>> useful. You could expose all the methods that were on protocol
>>>> extensions of Collection on this existential, unless they used
>>>> associated types other than the element type. But you couldn't pass the
>>>> existential to a generic function like
>>>>
>>>> func scrambled<C: Collection>(_ c: C) -> [C.Element]
>>>
>>> I don’t understand. Why couldn’t an existential be passed to that function?
>>>
>>> -Thorsten
>>>
>>>
>>>
>>>>
>>>>> IMO you should *have* to introduce unsafe behavior like that manually.
>>>>
>>>> Collection where Element == Int & Index == *
>>>>
>>>> ?
>>>>
>>>>> Collection indices are already something that isn’t fully statically
>>>>> safe so I understand why you might want to allow this.
>>>>
>>>> By the same measure, so are Ints :-)
>>>>
>>>> The fact that a type's methods have preconditions does *not* make it
>>>> “statically unsafe.”
>>>>
>>>>> But I don’t think having the language's existentials do this
>>>>> automatically is the right approach. Maybe there is another approach
>>>>> that could be used in targeted use cases where the less safe behavior
>>>>> makes sense and is carefully designed.
>>>>
>>>> Whether it makes sense or not really depends on the use-cases. There's
>>>> little point in generalizing existentials if the result isn't very useful.
>>>> The way to find out is to take a look at the examples we currently have
>>>> of protocols with associated types or Self requirements and consider
>>>> what you'd be able to do with their existentials if type relationships
>>>> couldn't be erased.
>>>>
>>>> We have known use-cases, currently emulated in the standard library, for
>>>> existentials with erased type relationships. *If* these represent the
>>>> predominant use cases for something like generalized existentials, it
>>>> seems to me that the language feature should support that. Note: I have
>>>> not seen anyone build an emulation of the other kind of generalized
>>>> existential. My theory: there's a good reason for that :-).
>>>>
>>>> --
>>>> Dave
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> --
> Dave
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

All of these are type errors. All we know about c1, c2, c3 and c4 is that they are of type `Any<Collection where Element == Int>` which does not constrain the Index type, so all we know is that each variable can have a different Index type. So, the type system can only say, no, these are type errors.

On a second thought this example reads a little bit like the following:

let x1: Object = …
let x2: Object = „hello"

// work/trap/nocompile?
x1.characters

// x2 happens to be a String
// work/trap/nocompile?
x2.characters

I think we all agree that all these are type errors even though we know that x2 contains a String and it might be useful to work in some cases. Maybe :-)

The same applies to the examples above IMO. The static knowledge is not sufficient for those examples to compile.

But thinking of path dependent types the following should work:

let c1: IntCollection = …
let c2: c1.Type = c1[3..<10]

subscript_(c1, c2.startIndex) // ok!

-Thorsten

···

Am 09.06.2016 um 00:18 schrieb Dave Abrahams via swift-evolution <swift-evolution@swift.org>:

Exactly. But much simpler cases will also either have to trap at
runtime or be prohibited outright:

func subscript_<C: Collection>(c: C, i: C.Index) -> C.Collection.Element {
   return c[i]
}

typealias IntCollection = Any<Collection where Element == Int>
let c1: IntCollection = ...
let c2: IntCollection = c1[3..<10]
let c3: IntCollection = ...
let c4: IntCollection = c1.reversed()

// Note: the underlying index types are the same, and are supposed to
// interoperate. What do you do (work/trap/nocompile)?
_ = subscript_(c1, c2.startIndex)

// The underlying index types happen to be the same, and are not
// supposed to interoperate. What do you do (silently “work”/trap/nocompile)?
_ = subscript_(c1, c3.startIndex)

// The underlying index types are different. What do you do (trap/nocompile)?
_ = subscript_(c1, c4.startIndex)

, but haven't realized
that if you step around the type relationships encoded in Self
requirements and associated types you end up with types that appear to
interoperate but in fact trap at runtime unless used in exactly the
right way.

Trap at runtime? How so? Generalized existentials should still be
type-safe.

There are two choices when you erase static type relationships:

1. Acheive type-safety by trapping at runtime

FloatingPoint(3.0 as Float) + FloatingPoint(3.0 as Double) // trap

2. Don't expose protocol requirements that involve these relationships,
which would prevent the code above from compiling and prevent
FloatingPoint from conforming to itself.

Or are you talking about the hypothetical types / behaviors people
think they want when they don’t fully understand what is happening...

I don't know what you mean here. I think generalized existentials will
be nice to have, but I think most people will want them to do something
they can't possibly do.

Exactly. What I meant is that people think they want that expression
to compile because they don’t understand that the only thing it can do
is trap. I said “hypothetical” because producing a compile time error
rather than a runtime trap is the only sane thing to do. Your comment
surprised me because I can’t imagine we would move forward in Swift
with the approach of trapping.

I would very much like to be able to create instances of “Collection
where Element == Int” so we can throw away the wrappers in the stdlib.
That will require some type mismatches to be caught at runtime via
trapping.

For invalid index because the existential accepts a type erased index?

Exactly.

How do you decide where to draw the line here? It feels like a very
slippery slope for a language where safety is a stated priority to
start adopting a strategy of runtime trapping for something as
fundamental as how you expose members on an existential.

If you don't do this, the alternative is that “Collection where Element
== Int” does not conform to Collection.

This isn’t directly related to having self or associated type
requirements. It is true of all existentials.

That is just an implementation limitation today, IIUC. What I'm talking
about here would make it impossible for some to do that.

If that changes for simple existentials and generalized existentials
expose all members (as in the latest draft of the proposal) maybe it
will be possible for all existentials to conform to their protocol.

Not without introducing runtime traps. See my “subscript function”
example.

That's weird and not very
useful. You could expose all the methods that were on protocol
extensions of Collection on this existential, unless they used
associated types other than the element type. But you couldn't pass the
existential to a generic function like

func scrambled<C: Collection>(_ c: C) -> [C.Element]

IMO you should *have* to introduce unsafe behavior like that manually.

Collection where Element == Int & Index == *

?

I didn’t mean directly through the type of the existential.

My question is, why not? That is still explicit.

One obvious mechanism for introducing unsafe behavior is to write
manual type erasure wrappers like we do today.

Another possibility would be to allow extending the existential type
(not the protocol). This would allow you to write overloads on the
Collection existential that takes some kind of type erased index if
that is what you want and either trap if you receive an invalid index
or better (IMO) return an `Element?`. I’m not sure how extensions on
existentials might be implemented, but this is an example of the kind
of operation you might want available on it that you wouldn’t want
available on all Collection types.

Collection indices are already something that isn’t fully statically
safe so I understand why you might want to allow this.

By the same measure, so are Ints :-)

The fact that a type's methods have preconditions does *not* make it
“statically unsafe.”

That depends on what you mean by safe. Sure, those methods aren’t
going corrupt memory, but they *are* going to explicitly and
intentionally crash for some inputs. That doesn’t qualify as “fully
safe” IMO.

Please pick a term other than “unsafe” here; it's not unsafe in the
sense we mean the word in Swift. It's safe in exactly the same way that
array indexes and integers are. When you violate a precondition, it
traps.

The user doesn't do anything “manual” to introduce that trapping
behavior for integers. Preconditions are a natural part of most types.

But I don’t think having the language's existentials do this
automatically is the right approach. Maybe there is another approach
that could be used in targeted use cases where the less safe behavior
makes sense and is carefully designed.

Whether it makes sense or not really depends on the use-cases. There's
little point in generalizing existentials if the result isn't very useful.

Usefulness depends on your perspective.

Of course. As I've said, let's look at the use cases.

I have run into several scenarios where they would be very useful
without needing to be prone to crashes when used incorrectly. One
obvious basic use case is storing things in a heterogenous collection
where you bind .

bind what?

The way to find out is to take a look at the examples we currently have
of protocols with associated types or Self requirements and consider
what you'd be able to do with their existentials if type relationships
couldn't be erased.

We have known use-cases, currently emulated in the standard library, for
existentials with erased type relationships. *If* these represent the
predominant use cases for something like generalized existentials, it
seems to me that the language feature should support that. Note: I have
not seen anyone build an emulation of the other kind of generalized
existential. My theory: there's a good reason for that :-).

AFAIK (and I could be wrong) the only rules in the language that
require the compiler to synthesize a trap except using a nil IUO, `!`
on a nil Optional, and an invalid `as` cast . These are all
syntactically explicit unsafe / dangerous operations. All other traps
are in the standard library (array index, overflow, etc). Most
important about all of these cases is that they have received direct
human consideration.

There is no distinction in the user model between what might be
synthesized by the language and what appears on standard library types.

Introducing a language (not library) mechanism that exposes members on
generalized existentials in a way that relies on runtime traps for
type safety feels to me like a pretty dramatic turn agains the stated
priority of safety. It will mean you must understand exactly what is
going on and be extremely careful to use generalized existentials
without causing crashes. This will either make Swift code much more
crashy or will scare people away from using generalized existentials
(and maybe both).

I don't accept either of those statements without seeing some analysis
of the use-cases. For example, I don't believe that AnyCollection et al
are particularly crash-prone. The likelihood that you'll use the wrong
index type with a collection is very, very low. I'm less certain of
what happens with Self requirements in real cases.

Neither of those outcomes is good.

Collection indices are a somewhat special case as there is already a
strong precondition that people are familiar with because it would be
too costly to performance and arguably too annoying to deal with an
Optional result in every array lookup. IMO that is why the library is
able to get away with it in the current type erased AnyCollection.
But this is not a good model for exposing any members on an
existential that do not already have a strong precondition that causes
a trap when violated.

I think a big reason why you maybe haven’t seen a lot of examples of
people writing type erased “existentials" is because it is a huge pain
in the neck to write this stuff manually today. People may be
designing around the need for them. I haven’t seen a huge sampling of
type erased “existentials" other people are writing but I haven’t
written any that introduce a trap like this. The only traps are in
the “abstract" base class whose methods will never be called (and
wouldn’t even be implemented if they could be marked abstract).

What specific things do you think we need to be able to do that rely
on the compiler synthesizing a trap in the way it exposes the members
of the existential?

I don't know. I'm saying, I don't think we understand the use-cases
well enough to make a determination.

Here are a few examples from Austin’s proposal that safely use
existential collections. I don’t understand why you think this
approach is insufficient. Maybe you could supply a concrete example
of a use case that can’t be written with the mechanism in Austin’s
proposal.

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure <https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure&gt;

let a : Any<Collection>

// A variable whose type is the Index associated type of the underlying
// concrete type of 'a'.
let theIndex : a.Index = ...

// A variable whose type is the Element associated type of the underlying
// concrete type of 'a'.
let theElement : a.Element = ...

// Given a mutable collection, swap its first and last items.
// Not a generic function.
func swapFirstAndLast(inout collection: Any<BidirectionalMutableCollection>) {
   // firstIndex and lastIndex both have type "collection.Index"
   guard let firstIndex = collection.startIndex,
       lastIndex = collection.endIndex?.predecessor(collection) where lastIndex != firstIndex else {
           print("Nothing to do")
           return
   }

   // oldFirstItem has type "collection.Element"
   let oldFirstItem = collection[firstIndex]

   collection[firstIndex] = collection[lastIndex]
   collection[lastIndex] = oldFirstItem
}

var a : Any<BidirectionalMutableCollection where .Element == String> = ...

let input = "West Meoley"

// Not actually necessary, since the compiler knows "a.Element" is String.
// A fully constrained anonymous associated type is synonymous with the concrete
// type it's forced to take on, and the two are interchangeable.
// However, 'as' casting is still available if desired.
let anonymousInput = input as a.Element

a[a.startIndex] = anonymousInput

// as mentioned, this also works:
a[a.startIndex] = input

// If the collection allows it, set the first element in the collection to a given string.
func setFirstElementIn(inout collection: Any<Collection> toString string: String) {
   if let element = string as? collection.Element {
       // At this point, 'element' is of type "collection.Element"
       collection[collection.startIndex] = element
   }
}

Neither of these look like they actually make *use* of the fact that
there's type erasure involved (and therefore should probably be written
as generics?). The interesting cases with Any<Collection...>, for the
purposes of this discussion, arise when you have multiple instances of
the same existential type that wrap different concrete types.

Another problem I see: in this new world, what is the model for choosing
whether to write a function as a protocol extension/generic, or as a
regular function taking existential parameters? Given that either of
the above could have been written either way, we need to be able to
answer that question. When existentials don't conform to their
protocols, it seems to me that the most general thing to do is use
existentials whenever you can, and only resort to using generics when
forced by the type system. This does not seem like a particularly good
programming model to me, but I might be convinced otherwise.

Anyway, my overall point is that this all seems like something we *can*
do and that nicely fills gaps in the type system, but not necessarily
something we *should* do until we better understand what it's actually
*for* and how it affects the programming model.

playing with a different syntax.

what’s missing is the syntax for opening such that it is possible to deal safely with the underlying concrete type (e.g. when dealing with 2 Equatable). But Doug had a nice strawman that can be streamlined.

···

On Jun 9, 2016, at 4:55 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Wed Jun 08 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 8, 2016, at 1:33 PM, Dave Abrahams <dabrahams@apple.com> wrote:
on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:

On Jun 7, 2016, at 9:15 PM, Dave Abrahams <dabrahams@apple.com> wrote:
on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 7, 2016, at 4:13 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Tue Jun 07 2016, Matthew Johnson <swift-evolution@swift.org> wrote:

--
Dave
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Regards
(From mobile)

Regards
(From mobile)

Ah, thanks, I forgot! I still consider this a bug, though (will have
to read up again what the reasons are for that behavior).

Yes, but in the case of the issue we're discussing, the choices are:

1. Omit from the existential's API any protocol requirements that depend
on Self or associated types, in which case it *can't* conform to
itself because it doesn't fulfill the requirements.

2. Erase type relationships and trap at runtime when they don't line up.

Matthew has been arguing against #2, but you can't “fix the bug” without
it.

#1 has been my preference for a while as well, at least as a starting
point.

I should point out that with the resyntaxing of existentials to
Any<Protocols...>, the idea that Collection's existential doesn't
conform to Collection becomes far less absurd than it was, so maybe this
is not so bad.

I think the problem is more that Any<Collection> does not conform to a specific value for a type parameter T: Collection

What I mean by this is that `Collection` denotes a type family, a generic parameter `T: Collection` denotes a specific (though unknown) member of that type family and `Any<Collection>` denotes the type family again, so there is really no point in writing Any<Collection> IMO.
The type family cannot conform to T because T is just one fixed member of it.
It conforms to itself, though, as I can write
let c1: Any<Collection> = …
let c2: Any<Collection> = c1

That’s why I think that we could just drop Any<Collection> and simply write Collection.

Let me expand that a bit:

Actually all this talk about existentials vs. generics or protocols vs. classes has had me confused somewhat and I think there are still some misconceptions present on this list sometimes, so I’ll try to clear them up:

(1) misconception: protocols with associated types are somehow very different from generics

I don’t think they are and I will explain why. The only difference is the way the type parameters are bound: generics use explicit parameter lists whereas protocols use inheritance. That has some advantages (think long parameter lists of generics) and some disadvantages.
These ways are dual in a notation sense: generic types have to have all parameters bound whereas protocols cannot bind any of them.
The „existential“ notation `Any<>` being discussed on this list is nothing more than adding the ability to protocols to bind the parameters to be used just like Java’s wildcards are adding the opposite feature to generics, namely not having to bind all parameters.

btw, i tried to to see if Any<> could have a simpler alternative
Extended existential · GitHub

As you know I like using `&` as type intersection operator. But you write "The syntax leave a void when it comes to expressing the so called Top type:“
Why? Just give the top type a name, e.g. `Any` and you are done.

Yes.. I just don't like magic. I think that all types should be expressable with a syntax and that you can then decide to alias one parsing case with a specific name. Not the other way around.

Well, I think that would be backwards, because we have a nominal type system. That means that the syntax for a type is just its name.

I realize we do not understand each other.
The words we use now to describe a behavior are just that, words. As is the concepts they describe are useless to the computer running the compiler.
So we need to map these concepts into a heuristic that a fast but dumb computer will be able to reason with. The code to do that will be either clean and organized, or it will look contrived and full of different paths that will be difficult to mesh together. Of all the possible ways in which swift can behave as a language, some will lead to the former, others to the latter code. I think this is not fate or something you find out after the decision was made and you struggle to carry it through. This is something that can partially be predicted. One of the tools for such prediction is to translate the concepts into a grammar that will show formalize the logic. The simpler the logic, the cleaner (not simple) the final code.

Saying that the syntax for a type is a name is of no use whatsoever for the compiler implementer. It is so universally true that it cannot help in any way whatsoever decide the shape of the swift grammar, much less the structure of the c++ code implementing it.

`&` is a type operator which creates a new type from its operands.
`where` clauses add constraints to types.
But it all starts with a type's name.

The only magic would be that all type definitions (`protocol` etc.) which do not give a supertype they conform to, will implicitly conform to `Any`, i.e.

I call magic the core notions of swift that cannot be expressed in swift but have to parachutted in by the compiler. If you read Chris' initial message you will see that he said as much, that adopting P&Q as syntax was leaving a gap that the compiler would have to magically fill.

protocol Foo { … }

means

protocol Foo : Any { … }

That’s less magic than creating a special syntax just to express the top type.

I will try again... Would it be easier to understand it if instead of magic I said arbitrary? You are creating a special case: you decide arbitrarily that the special series of characters 'A' 'n' 'y' with this precise casing and without any spaces is going to be adorned with a special meaning when used in specific situations. This is not something you deduced. My approach is exactly the opposite: _ does not describe the top type because I have a special affinity with these characters, it describes the top type because it is the only possible logical conclusion from having followed a set of rules attached to a syntax that describes ALL existentials. And because it is not particularly savory, I typealias it to something else. But this is not in the compiler, it us in the standard linrary... inside swift, not outside.

Ok, that's a good argument. But your proposal does not contain those rules.
Why should _ describe the top type? You just say this in your proposal, but you do not define rules what _ means.
_[A,B]

don't know where this comes from. This is incorrect and the parser would reject it.

describes the type intersection of A and B, i.e. it contains all members of A which are also members of B. _ does not list any types, so one might conclude that it has no members and therefore must be the bottom type!

Are you saying that protocol<> is the bottom type of Swift then? Because if you disect the logic it follows to explain why it is a top type and apply the exact same reasoning to _ then you should reach the same conclusion {same effects produce the same conclusions}

Or _[A,B] in reality means _[A,B,Any],i.e. A & B & Any which of course is equal to A & B. Then _ would just mean Any, but we would have had to introduce the top type explicitly again.

From cause to effects. Any is an effect (a conclusion we reach) rather than a cause (something we posit before starting the discussion). Like I explained, Any is a stdlib provided convenient typealias for _ as opposed to a parachuted concept born inside the compiler. Btw, it means a different stdlib could redefine it as All, without altering the compiler. Any is not core to swift's structure, but _ is because it is borne out of the mechanical uniformity of the grammar.

On the other hand if we declare a protocol Any and define protocol A {...} to mean protocol A: Any {...} there is not really something special in the compiler except for adding the conformance which seems very minor to me. Everything else is just standard behavior of the type system.

You are back to adding some magic that I just demonstrated is not required if the grammar is simple (not that i could add it to my grammar to please you, but then it would not eliminate any of the other definitions, and would just complicate the parser and type checker to ensure that people use it correctly. Per Ockham's principle, the system i describe is a better alternative.

Read Chris' original announcement.. He describes P&Q as it would be adopted today as being just a CORNER CASE of a single general principal... a single grammar that can work today to describe P&Q as well as generalize existentials tomorrow. No special treatment, single parser, single rule. I don't like to write un-necessary IF statements in code.

Sorry, I do not understand what you mean here: where would you have to write unnecessary if-statements?

Close your eyes and make a mental representation of what the code to implement the Any<....> proposal will necessarily look like... that is what I am referring to. That code cannot not be uggly. Because some of of things a parser typically should catch are not discernsble, and therefore have to be differed to the type checker. By comparison, look at the grammar I wrote and picture the code to implement it. The picture will look very different, and the parser can already eliminate a lot of things that never reach the type checker.

Why should the top type have special *syntax*? It is just the type all other types conform to. No need to do something special here and therefore no need to invent an alternative syntax like `Any<>` or the alternative from your gist which is rather confusing IMO (and I don’t like the special case given to classes in the syntax).

The _ case is just a degenerate case of the syntax, showing that it is expressible inside, as opposoed to have to define a contextual keyword (read the SourceKit code). And then it is aliasable. Part of the problems in swift today to me is that some things are no doable in swift, so the compiler must contain that semantic. This here is just one example, but there are others. These are notions that the standard library has to defer to the compiler. Some of them have been tabled for 4.0 it seems. My point here was that it is not fate, and choosing the syntax carefully for existentials (whichever it is, i don't really care inthe end) would prevent having to add a magic keyword for to top type to the compiler and to SourceKit again (they are trying to remove them).

`Any` would *not* be a keyword. It is just a type name like `Collection` or `Int`. Like I said, the only magic would be in adding `: Any` to type definitions without a conforming clause.

I hope that by now you understand what magic I was talking about.

As for the current Any<...> proposal for generalizing existentials it is IMHO cluncky and magic. There is nothing stopping us mechanically from entering Any<UITableView, UIButton>. To me that is the holemark of bad design. In what I tested you just can't do it and the reason is clear. Of course when I

`Any<UITableView, UIButton>` is just an intersection type and would be written UITableView & UIButton. And, yes, that would be ok, because it would be just the empty set, i.e. the bottom type (which has no members). There is no magic involved, it is just the normal result of an intersection: each type is a set containing all instances of this type (instances conforming to it)

You can't be serious? You are saying that to you the ide should not be telling us we are writting an absurdity? And this one was obvious, but it gets a lot worse with more complex types.

Take a screenshot next time you see a "let x:Any<UITableView, UIButton>" in an app and send it to me. The absurdity would be for the compiler to generate a binary, the app to start, and us to wonder why x the code depending on x would never execute. Seems obvious to you why now?

I’m taking an example from Gavin King’s presentation about Ceylon instead which can be found here: https://greenjug.java.net/resources/CeylonEastCoastTour20141016.pdf

Object is the abstract supertype of all types representing definite values, i.e. which are not null. Remember that in Ceylon Optional<T> is modeled as type union T | Null, where Null is the type containing the single member null (i.e. nil in Swift).
Element is the type parameter for the elements of Iterable.

Element might be String | Null, for example (in Ceylon abbreviated as String? just like in Swift).
Element & Object can only be String, because Null & Object is empty, i.e. is the bottom type:

(String | Null) & Object = (String & Object) | (Null & Object) = String | Bottom = String

So, while you don’t see UITableView & UIButton here, there is Null & Object at work which yields the bottom type, just like UITableView & UIButton would.
If this was forbidden, then how would the type system be able to simplify the expression (String | Null) & Object?

not debatting past here I can't see how it relates (it is interesting but you may want to look at the whole thing from a 'whats usefull' perspective, rather than as 'whats combinatorially possible‘

Like the slide above hopefully demonstrated this is not just a nice theoretical exercise but it *is* useful if you use the whole abstraction and do not try to arbitrarily single out special cases as forbidden.

Of course, writing UITableView & UIButton is not useful,

you had me wondering for a moment

As for ceylon, it has come a long way and is great, but i don't like it anymore today than I did 4 years ago (all along i always preferred kotlin). Professionally, my poisons of choice are c, c++, java, scala, c#, xtend, go, objc, swift, javascript(-), typescript(+), and the venerable (but turing complete ;-)) sql, so that might give you a sense of my bias.

Cheers

···

On Jun 12, 2016, at 4:05 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 12.06.2016 um 09:21 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com>:
On Jun 11, 2016, at 11:43 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 11.06.2016 um 15:08 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com>:

On Jun 11, 2016, at 2:05 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 11.06.2016 um 12:38 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com>:

On Jun 11, 2016, at 11:30 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 11.06.2016 um 08:00 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com>:

On Jun 10, 2016, at 9:35 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 09.06.2016 um 19:50 schrieb Thorsten Seitz via swift-evolution <swift-evolution@swift.org>:

Am 09.06.2016 um 18:49 schrieb Dave Abrahams via swift-evolution <swift-evolution@swift.org>:
on Wed Jun 08 2016, Jordan Rose <swift-evolution@swift.org> wrote:

On Jun 8, 2016, at 13:16, Dave Abrahams via swift-evolution >>>>>>>>>>>>> <swift-evolution@swift.org> wrote:
on Wed Jun 08 2016, Thorsten Seitz >>>>>>>>>>>> >>>>>>>>>>>>> <swift-evolution@swift.org >>>>>>>>>>>>> <mailto:swift-evolution@swift.org>> >>>>>>>>>>>>> wrote:

but writing something like T & Object is, but it needs the whole general mechanism to work.
(And that general mechanism will show you automatically that UITableView & UIButton is not useful, because you can’t call any method on it, *without* having to introduce compiler magic or IDE magic to explicitly forbid this.)

Hope that made sense now :-)

-Thorsten

Why is an empty intersection absurd? The beauty is that all type expressions sort out automatically by applying simple set rules. And it actually does make sense!

struct Set<T> {
   // answer any one element
   func any() -> T?
}
func intersect<T, U>(a: Set<T>, b: Set<U>) -> Set<T & U> {...}

let x: Set<UITableView> = ...
let y: Set<UIButton> = ..
let z = intersect(x, y) // has type Set<Bottom>
z.any // has type Optional<Bottom> and therefore can only be nil

Therefore the type system statically knows that the intersection of those sets is always empty. No need to declare this as invalid. It just works correctly and gives the results you would expect without need for special casing and compiler magic (which we both would like to keep small).

-Thorsten

. Intersecting two sets might result in an empty set. The type denoting the empty set is the bottom type which is the subtype of all types and might be called `Nothing` or `Never` or `Bottom`.
Ceylon makes very nice use of type intersections and type unions and the beautiful thing is that these type operations really just work like you would expect if you think of types as sets (which is the standard definition for a type AFAIK). No surprises and no magic there!

So it is *not* a sign of bad design, quite to the contrary! What did you test it with? Probably not Ceylon, because otherwise you would have seen that it just works.

Hey, who knows, ceylon may one day come to llvm...

say you can't I do not mean that your keyboard will zap u if you try. But the way to verify correctness is different... and in a world where xcode would be a good ide, then code completion would even show you why and actually truly assist (because the grammar i tested makes really makes it possible). I know that xcode is only as good as SourceKit lets it be.

Essentially `Any<Collection>` in Swift is just the same as `Collection<?>` in Java (assuming for comparability’s sake that Swift’s Collection had no additional associated types; otherwise I would just have to introduce a Collection<Element, Index> in Java).

Likewise `Any<Collection where .Element: Number>` is just the same as `Collection<? extends Number>` in Java.

Java supports co/contra variant params

Java has no declaration-site variance like Ceylon or Scala have (see e.g. Eclipse Ceylon™ | projects.eclipse.org).

Java’s wildcards are a way to express use-site variance. The proposed `Any<>` does just the same.

And just like Collection<?> does not conform to a type parameter `T extends Collection<?>` because Collection<?> is the type `forall E. Collection<E>` whereas `T extends Collection<?>` is the type `T. Collection<T>` for a given T.

This picture is accurate today, but there are going to be more serious differences after 10 no date is currently geven for when it will come)

You mean Java 10?

Yes. After 10. Than is their only date hint.

In essence protocols with associated types are like generics with wildcards.

Yes, java has kept everything within its generics system rather than split parts out. Something people may not immediately think about with respect to the 2 generic systems is that when u call a func<T>capture(T t){} in java with a wildcard you are doing a compile time capture only (to avoid the dreaded unsafe casts), whereas it is really nice to do the same in swift and subsequently be able to access T.Type and see that it is not Any. The closest u ever get to that type at runtime in java is via generics introspection, but u still can't do everything ( like no new T() ). But today the bridging between existential types and generics is definitely a work in progress.

Coming back to the questions whether (a) allowing existentials to be used as types is useful and (b) whether sacrificing type safety would somehow be necessary for that, I think we can safely answer
(a) yes, it *is* useful to be able to use existentials like Any<Collection> as types, because wildcards are quite often needed and very useful in Java (they haven’t been added without a reason)

IMO they made java 8 (referring to streams). And even though the syntax for co/contra variance is pretty heavy, it is the foundation for all modern java code. The any-fication of the generics is going to open new doors as some of it will translate into a partial reification in the jvm. It seems the decision for now is to not use the extra info in java to retain binary compatibility with all the erased code out there, this is something scala might use in areas where it won't mind loosing java compatibility.

(b) no, sacrificing type safety does not make sense, as the experience with Java’s wildcards shows that this is not needed. Especially if something like path dependent types is used like proposed and some notation to open an existential’s type is added, which is both something that Java does not have.

I hope typesafe opening inside the " if let " syntax gets added. I know that chris is against sugaring, but I played if an implementation of

... x is String?
If let! x {}
That runs as
if let x = x {}

something equally short could be done here.

(2) misconception: POP is different from OOP

It is not. Protocols are just interfaces using subtyping like OOP has always done. They just use associated types instead of explicit type parameters for generics (see above). The more important distinction of Swift is emphasizing value types and making mutation safely available by enforcing copy semantics for value types.

Values are coming to the jvm, which will narrow this gap (like their view identity for value vs ref and the whole deep ==) . I also really like the cow approach of the swift runtime.

But protocols are not really different from interfaces in Java.

There is one big difference: default methods, but it seems swift will add that soon.

Swift already has default extension methods, doesn’t it?

Yes, and no. It has differently dispatched code that can be found to fill in the gap of the conformance req, yes. But Joe Grof (?) said that there are no reasons why these could not be added. Having true defaults could be one way to deal with optional comformance…

The dispatch issue only arises for extensions introducing a method that is not declared in a protocol. That is something you *cannot* do in Java. Java’s default methods are implementations for methods declared in interfaces. Swift’s extension methods providing defaults for methods declared in a protocol are dynamically dispatched and should work like Java’s default methods.

I think we should agree to disagree.

(One caveat exists with subclasses where the superclasses didn’t implement the method because I then am not allowed to `override` the default method but that is a bug IMO).

-Thorsten

protocol MyProto {
func mustImplement()
default canImplement() { }
}

Related data there:
[swift-evolution] [summary] Protocol extension method dispatch - was [Static Dispatch Pitfalls]

I also really like how extensions and conformance mix together in swift to bake retro-modelling in and the adapter pattern (spent enough years deep diving inside eclipse to appreciate it).

I would have preferred a unified model using just classes with real multiple inheritance like Eiffel has and value types just being a part of that similar to Eiffel’s `expanded` classes. But that ship has probably sailed a long time ago :-/

I like extensions very much (having used Smalltalk for a long time). I like enums for the pattern matching and structs as value types. But having to split protocols off instead of using abstract classes makes things more complicated IMO.

-1 i am old school c/c++... i really like protocol, struct, class, extensions, enums. It is a really nice mix that gives objc people room to grow, but I do miss how they are an integral part of generics (i protocols as a replacement and look forward to when they interact better) and namespaces+scoped-imports (c#)... Looking forward to where things go next

Yeah, namespacing/submodules/conflict resolution (when doing imports but also when conforming to multiple protocols which has just the same problems) are still missing. But I’m optimistic :-) Let’s complete generics first, then tackle existentials/type intersections.

I think I care more about existentials, but only because of the kind of java i wrote for a living. I just looked at a bunch of opensource swift libs (particularly for server side swift)... some of it is a real engineering disaster: 20+ folders, each with 2 source files... or 3 folders "extensions" "utils" "classes". Swift is currently not equiped for people to write big things with... I wish the team would address it sooner than later (look at their own c++ code to see the difference). IMHO import conflicts are more often the symptom of bad code than a real issue.

Alas, most languages suffer from poor module systems. A good module system should not only allow resolving conflicts between modules and making it possible to structure code well, but solve the issue around versioned modules so that is is possibly to safely use different versions of the same module in the same application.

-Thorsten

Cheers

-THorsten

So be it. But at least there should be no reasons for POP vs OOP wars ;-)
(I’d like to add that I liked Dave’s talks at last WWDC very much, it’s just that I don’t think that POP is something new or different.)

I used to thin that way. But today I think that although in broad brush strokes the similarities and bigger than the differences, there is room for making a bigger difference in the how.

-Thorsten

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Regards
(From mobile)

Regards
(From mobile)

Ah, thanks, I forgot! I still consider this a bug, though (will have
to read up again what the reasons are for that behavior).

Yes, but in the case of the issue we're discussing, the choices are:

1. Omit from the existential's API any protocol requirements that depend
on Self or associated types, in which case it *can't* conform to
itself because it doesn't fulfill the requirements.

2. Erase type relationships and trap at runtime when they don't line up.

Matthew has been arguing against #2, but you can't “fix the bug” without
it.

#1 has been my preference for a while as well, at least as a starting
point.

I should point out that with the resyntaxing of existentials to
Any<Protocols...>, the idea that Collection's existential doesn't
conform to Collection becomes far less absurd than it was, so maybe this
is not so bad.

I think the problem is more that Any<Collection> does not conform to a specific value for a type parameter T: Collection

What I mean by this is that `Collection` denotes a type family, a generic parameter `T: Collection` denotes a specific (though unknown) member of that type family and `Any<Collection>` denotes the type family again, so there is really no point in writing Any<Collection> IMO.
The type family cannot conform to T because T is just one fixed member of it.
It conforms to itself, though, as I can write
let c1: Any<Collection> = …
let c2: Any<Collection> = c1

That’s why I think that we could just drop Any<Collection> and simply write Collection.

Let me expand that a bit:

Actually all this talk about existentials vs. generics or protocols vs. classes has had me confused somewhat and I think there are still some misconceptions present on this list sometimes, so I’ll try to clear them up:

(1) misconception: protocols with associated types are somehow very different from generics

I don’t think they are and I will explain why. The only difference is the way the type parameters are bound: generics use explicit parameter lists whereas protocols use inheritance. That has some advantages (think long parameter lists of generics) and some disadvantages.
These ways are dual in a notation sense: generic types have to have all parameters bound whereas protocols cannot bind any of them.
The „existential“ notation `Any<>` being discussed on this list is nothing more than adding the ability to protocols to bind the parameters to be used just like Java’s wildcards are adding the opposite feature to generics, namely not having to bind all parameters.

btw, i tried to to see if Any<> could have a simpler alternative
Extended existential · GitHub

As you know I like using `&` as type intersection operator. But you write "The syntax leave a void when it comes to expressing the so called Top type:“
Why? Just give the top type a name, e.g. `Any` and you are done.

Yes.. I just don't like magic. I think that all types should be expressable with a syntax and that you can then decide to alias one parsing case with a specific name. Not the other way around.

Well, I think that would be backwards, because we have a nominal type system. That means that the syntax for a type is just its name.

I realize we do not understand each other.
The words we use now to describe a behavior are just that, words. As is the concepts they describe are useless to the computer running the compiler.
So we need to map these concepts into a heuristic that a fast but dumb computer will be able to reason with. The code to do that will be either clean and organized, or it will look contrived and full of different paths that will be difficult to mesh together. Of all the possible ways in which swift can behave as a language, some will lead to the former, others to the latter code. I think this is not fate or something you find out after the decision was made and you struggle to carry it through. This is something that can partially be predicted. One of the tools for such prediction is to translate the concepts into a grammar that will show formalize the logic. The simpler the logic, the cleaner (not simple) the final code.

Saying that the syntax for a type is a name is of no use whatsoever for the compiler implementer. It is so universally true that it cannot help in any way whatsoever decide the shape of the swift grammar, much less the structure of the c++ code implementing it.

`&` is a type operator which creates a new type from its operands.
`where` clauses add constraints to types.
But it all starts with a type's name.

The only magic would be that all type definitions (`protocol` etc.) which do not give a supertype they conform to, will implicitly conform to `Any`, i.e.

I call magic the core notions of swift that cannot be expressed in swift but have to parachutted in by the compiler. If you read Chris' initial message you will see that he said as much, that adopting P&Q as syntax was leaving a gap that the compiler would have to magically fill.

protocol Foo { … }

means

protocol Foo : Any { … }

That’s less magic than creating a special syntax just to express the top type.

I will try again... Would it be easier to understand it if instead of magic I said arbitrary? You are creating a special case: you decide arbitrarily that the special series of characters 'A' 'n' 'y' with this precise casing and without any spaces is going to be adorned with a special meaning when used in specific situations. This is not something you deduced. My approach is exactly the opposite: _ does not describe the top type because I have a special affinity with these characters, it describes the top type because it is the only possible logical conclusion from having followed a set of rules attached to a syntax that describes ALL existentials. And because it is not particularly savory, I typealias it to something else. But this is not in the compiler, it us in the standard linrary... inside swift, not outside.

Ok, that's a good argument. But your proposal does not contain those rules.
Why should _ describe the top type? You just say this in your proposal, but you do not define rules what _ means.
_[A,B]

don't know where this comes from. This is incorrect and the parser would reject it.

describes the type intersection of A and B, i.e. it contains all members of A which are also members of B. _ does not list any types, so one might conclude that it has no members and therefore must be the bottom type!

Are you saying that protocol<> is the bottom type of Swift then? Because if you disect the logic it follows to explain why it is a top type and apply the exact same reasoning to _ then you should reach the same conclusion {same effects produce the same conclusions}

Or _[A,B] in reality means _[A,B,Any],i.e. A & B & Any which of course is equal to A & B. Then _ would just mean Any, but we would have had to introduce the top type explicitly again.

From cause to effects. Any is an effect (a conclusion we reach) rather than a cause (something we posit before starting the discussion). Like I explained, Any is a stdlib provided convenient typealias for _ as opposed to a parachuted concept born inside the compiler. Btw, it means a different stdlib could redefine it as All, without altering the compiler. Any is not core to swift's structure, but _ is because it is borne out of the mechanical uniformity of the grammar.

On the other hand if we declare a protocol Any and define protocol A {...} to mean protocol A: Any {...} there is not really something special in the compiler except for adding the conformance which seems very minor to me. Everything else is just standard behavior of the type system.

You are back to adding some magic that I just demonstrated is not required if the grammar is simple (not that i could add it to my grammar to please you, but then it would not eliminate any of the other definitions, and would just complicate the parser and type checker to ensure that people use it correctly. Per Ockham's principle, the system i describe is a better alternative.

Read Chris' original announcement.. He describes P&Q as it would be adopted today as being just a CORNER CASE of a single general principal... a single grammar that can work today to describe P&Q as well as generalize existentials tomorrow. No special treatment, single parser, single rule. I don't like to write un-necessary IF statements in code.

Sorry, I do not understand what you mean here: where would you have to write unnecessary if-statements?

Close your eyes and make a mental representation of what the code to implement the Any<....> proposal will necessarily look like... that is what I am referring to. That code cannot not be uggly. Because some of of things a parser typically should catch are not discernsble, and therefore have to be differed to the type checker. By comparison, look at the grammar I wrote and picture the code to implement it. The picture will look very different, and the parser can already eliminate a lot of things that never reach the type checker.

Why should the top type have special *syntax*? It is just the type all other types conform to. No need to do something special here and therefore no need to invent an alternative syntax like `Any<>` or the alternative from your gist which is rather confusing IMO (and I don’t like the special case given to classes in the syntax).

The _ case is just a degenerate case of the syntax, showing that it is expressible inside, as opposoed to have to define a contextual keyword (read the SourceKit code). And then it is aliasable. Part of the problems in swift today to me is that some things are no doable in swift, so the compiler must contain that semantic. This here is just one example, but there are others. These are notions that the standard library has to defer to the compiler. Some of them have been tabled for 4.0 it seems. My point here was that it is not fate, and choosing the syntax carefully for existentials (whichever it is, i don't really care inthe end) would prevent having to add a magic keyword for to top type to the compiler and to SourceKit again (they are trying to remove them).

`Any` would *not* be a keyword. It is just a type name like `Collection` or `Int`. Like I said, the only magic would be in adding `: Any` to type definitions without a conforming clause.

I hope that by now you understand what magic I was talking about.

As for the current Any<...> proposal for generalizing existentials it is IMHO cluncky and magic. There is nothing stopping us mechanically from entering Any<UITableView, UIButton>. To me that is the holemark of bad design. In what I tested you just can't do it and the reason is clear. Of course when I

`Any<UITableView, UIButton>` is just an intersection type and would be written UITableView & UIButton. And, yes, that would be ok, because it would be just the empty set, i.e. the bottom type (which has no members). There is no magic involved, it is just the normal result of an intersection: each type is a set containing all instances of this type (instances conforming to it)

You can't be serious? You are saying that to you the ide should not be telling us we are writting an absurdity? And this one was obvious, but it gets a lot worse with more complex types.

Take a screenshot next time you see a "let x:Any<UITableView, UIButton>" in an app and send it to me. The absurdity would be for the compiler to generate a binary, the app to start, and us to wonder why x the code depending on x would never execute. Seems obvious to you why now?

I’m taking an example from Gavin King’s presentation about Ceylon instead which can be found here: https://greenjug.java.net/resources/CeylonEastCoastTour20141016.pdf

my understanding is that dave is the type system designer. I will let the two of you finish this conversation and just read.

very best

···

On Jun 12, 2016, at 4:05 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 12.06.2016 um 09:21 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com <mailto:laurent.mihalkovic@gmail.com>>:
On Jun 11, 2016, at 11:43 PM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

Am 11.06.2016 um 15:08 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com <mailto:laurent.mihalkovic@gmail.com>>:

On Jun 11, 2016, at 2:05 PM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

Am 11.06.2016 um 12:38 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com <mailto:laurent.mihalkovic@gmail.com>>:

On Jun 11, 2016, at 11:30 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

Am 11.06.2016 um 08:00 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com <mailto:laurent.mihalkovic@gmail.com>>:

On Jun 10, 2016, at 9:35 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Am 09.06.2016 um 19:50 schrieb Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Am 09.06.2016 um 18:49 schrieb Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
on Wed Jun 08 2016, Jordan Rose <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 8, 2016, at 13:16, Dave Abrahams via swift-evolution >>>>>>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
on Wed Jun 08 2016, Thorsten Seitz >>>>>>>>>>>> >>>>>>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>>>>>>>>> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> >>>>>>>>>>>>> wrote:

<PastedGraphic-1.tiff>

Object is the abstract supertype of all types representing definite values, i.e. which are not null. Remember that in Ceylon Optional<T> is modeled as type union T | Null, where Null is the type containing the single member null (i.e. nil in Swift).
Element is the type parameter for the elements of Iterable.

Element might be String | Null, for example (in Ceylon abbreviated as String? just like in Swift).
Element & Object can only be String, because Null & Object is empty, i.e. is the bottom type:

(String | Null) & Object = (String & Object) | (Null & Object) = String | Bottom = String

So, while you don’t see UITableView & UIButton here, there is Null & Object at work which yields the bottom type, just like UITableView & UIButton would.
If this was forbidden, then how would the type system be able to simplify the expression (String | Null) & Object?

not debatting past here I can't see how it relates (it is interesting but you may want to look at the whole thing from a 'whats usefull' perspective, rather than as 'whats combinatorially possible‘

Like the slide above hopefully demonstrated this is not just a nice theoretical exercise but it *is* useful if you use the whole abstraction and do not try to arbitrarily single out special cases as forbidden.

Of course, writing UITableView & UIButton is not useful, but writing something like T & Object is, but it needs the whole general mechanism to work.
(And that general mechanism will show you automatically that UITableView & UIButton is not useful, because you can’t call any method on it, *without* having to introduce compiler magic or IDE magic to explicitly forbid this.)

Hope that made sense now :-)

-Thorsten

Why is an empty intersection absurd? The beauty is that all type expressions sort out automatically by applying simple set rules. And it actually does make sense!

struct Set<T> {
   // answer any one element
   func any() -> T?
}
func intersect<T, U>(a: Set<T>, b: Set<U>) -> Set<T & U> {...}

let x: Set<UITableView> = ...
let y: Set<UIButton> = ..
let z = intersect(x, y) // has type Set<Bottom>
z.any // has type Optional<Bottom> and therefore can only be nil

Therefore the type system statically knows that the intersection of those sets is always empty. No need to declare this as invalid. It just works correctly and gives the results you would expect without need for special casing and compiler magic (which we both would like to keep small).

-Thorsten

. Intersecting two sets might result in an empty set. The type denoting the empty set is the bottom type which is the subtype of all types and might be called `Nothing` or `Never` or `Bottom`.
Ceylon makes very nice use of type intersections and type unions and the beautiful thing is that these type operations really just work like you would expect if you think of types as sets (which is the standard definition for a type AFAIK). No surprises and no magic there!

So it is *not* a sign of bad design, quite to the contrary! What did you test it with? Probably not Ceylon, because otherwise you would have seen that it just works.

Hey, who knows, ceylon may one day come to llvm...

say you can't I do not mean that your keyboard will zap u if you try. But the way to verify correctness is different... and in a world where xcode would be a good ide, then code completion would even show you why and actually truly assist (because the grammar i tested makes really makes it possible). I know that xcode is only as good as SourceKit lets it be.

Essentially `Any<Collection>` in Swift is just the same as `Collection<?>` in Java (assuming for comparability’s sake that Swift’s Collection had no additional associated types; otherwise I would just have to introduce a Collection<Element, Index> in Java).

Likewise `Any<Collection where .Element: Number>` is just the same as `Collection<? extends Number>` in Java.

Java supports co/contra variant params

Java has no declaration-site variance like Ceylon or Scala have (see e.g. Eclipse Ceylon™ | projects.eclipse.org).

Java’s wildcards are a way to express use-site variance. The proposed `Any<>` does just the same.

And just like Collection<?> does not conform to a type parameter `T extends Collection<?>` because Collection<?> is the type `forall E. Collection<E>` whereas `T extends Collection<?>` is the type `T. Collection<T>` for a given T.

This picture is accurate today, but there are going to be more serious differences after 10 no date is currently geven for when it will come)

You mean Java 10?

Yes. After 10. Than is their only date hint.

In essence protocols with associated types are like generics with wildcards.

Yes, java has kept everything within its generics system rather than split parts out. Something people may not immediately think about with respect to the 2 generic systems is that when u call a func<T>capture(T t){} in java with a wildcard you are doing a compile time capture only (to avoid the dreaded unsafe casts), whereas it is really nice to do the same in swift and subsequently be able to access T.Type and see that it is not Any. The closest u ever get to that type at runtime in java is via generics introspection, but u still can't do everything ( like no new T() ). But today the bridging between existential types and generics is definitely a work in progress.

Coming back to the questions whether (a) allowing existentials to be used as types is useful and (b) whether sacrificing type safety would somehow be necessary for that, I think we can safely answer
(a) yes, it *is* useful to be able to use existentials like Any<Collection> as types, because wildcards are quite often needed and very useful in Java (they haven’t been added without a reason)

IMO they made java 8 (referring to streams). And even though the syntax for co/contra variance is pretty heavy, it is the foundation for all modern java code. The any-fication of the generics is going to open new doors as some of it will translate into a partial reification in the jvm. It seems the decision for now is to not use the extra info in java to retain binary compatibility with all the erased code out there, this is something scala might use in areas where it won't mind loosing java compatibility.

(b) no, sacrificing type safety does not make sense, as the experience with Java’s wildcards shows that this is not needed. Especially if something like path dependent types is used like proposed and some notation to open an existential’s type is added, which is both something that Java does not have.

I hope typesafe opening inside the " if let " syntax gets added. I know that chris is against sugaring, but I played if an implementation of

... x is String?
If let! x {}
That runs as
if let x = x {}

something equally short could be done here.

(2) misconception: POP is different from OOP

It is not. Protocols are just interfaces using subtyping like OOP has always done. They just use associated types instead of explicit type parameters for generics (see above). The more important distinction of Swift is emphasizing value types and making mutation safely available by enforcing copy semantics for value types.

Values are coming to the jvm, which will narrow this gap (like their view identity for value vs ref and the whole deep ==) . I also really like the cow approach of the swift runtime.

But protocols are not really different from interfaces in Java.

There is one big difference: default methods, but it seems swift will add that soon.

Swift already has default extension methods, doesn’t it?

Yes, and no. It has differently dispatched code that can be found to fill in the gap of the conformance req, yes. But Joe Grof (?) said that there are no reasons why these could not be added. Having true defaults could be one way to deal with optional comformance…

The dispatch issue only arises for extensions introducing a method that is not declared in a protocol. That is something you *cannot* do in Java. Java’s default methods are implementations for methods declared in interfaces. Swift’s extension methods providing defaults for methods declared in a protocol are dynamically dispatched and should work like Java’s default methods.

I think we should agree to disagree.

(One caveat exists with subclasses where the superclasses didn’t implement the method because I then am not allowed to `override` the default method but that is a bug IMO).

-Thorsten

protocol MyProto {
func mustImplement()
default canImplement() { }
}

Related data there:
[swift-evolution] [summary] Protocol extension method dispatch - was [Static Dispatch Pitfalls]

I also really like how extensions and conformance mix together in swift to bake retro-modelling in and the adapter pattern (spent enough years deep diving inside eclipse to appreciate it).

I would have preferred a unified model using just classes with real multiple inheritance like Eiffel has and value types just being a part of that similar to Eiffel’s `expanded` classes. But that ship has probably sailed a long time ago :-/

I like extensions very much (having used Smalltalk for a long time). I like enums for the pattern matching and structs as value types. But having to split protocols off instead of using abstract classes makes things more complicated IMO.

-1 i am old school c/c++... i really like protocol, struct, class, extensions, enums. It is a really nice mix that gives objc people room to grow, but I do miss how they are an integral part of generics (i protocols as a replacement and look forward to when they interact better) and namespaces+scoped-imports (c#)... Looking forward to where things go next

Yeah, namespacing/submodules/conflict resolution (when doing imports but also when conforming to multiple protocols which has just the same problems) are still missing. But I’m optimistic :-) Let’s complete generics first, then tackle existentials/type intersections.

I think I care more about existentials, but only because of the kind of java i wrote for a living. I just looked at a bunch of opensource swift libs (particularly for server side swift)... some of it is a real engineering disaster: 20+ folders, each with 2 source files... or 3 folders "extensions" "utils" "classes". Swift is currently not equiped for people to write big things with... I wish the team would address it sooner than later (look at their own c++ code to see the difference). IMHO import conflicts are more often the symptom of bad code than a real issue.

Alas, most languages suffer from poor module systems. A good module system should not only allow resolving conflicts between modules and making it possible to structure code well, but solve the issue around versioned modules so that is is possibly to safely use different versions of the same module in the same application.

-Thorsten

Cheers

-THorsten

So be it. But at least there should be no reasons for POP vs OOP wars ;-)
(I’d like to add that I liked Dave’s talks at last WWDC very much, it’s just that I don’t think that POP is something new or different.)

I used to thin that way. But today I think that although in broad brush strokes the similarities and bigger than the differences, there is room for making a bigger difference in the how.

-Thorsten

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Exactly. But much simpler cases will also either have to trap at
runtime or be prohibited outright:

func subscript_<C: Collection>(c: C, i: C.Index) -> C.Collection.Element {
   return c[i]
}

typealias IntCollection = Any<Collection where Element == Int>
let c1: IntCollection = ...
let c2: IntCollection = c1[3..<10]
let c3: IntCollection = ...
let c4: IntCollection = c1.reversed()

// Note: the underlying index types are the same, and are supposed to
// interoperate. What do you do (work/trap/nocompile)?
_ = subscript_(c1, c2.startIndex)

// The underlying index types happen to be the same, and are not
// supposed to interoperate. What do you do (silently “work”/trap/nocompile)?
_ = subscript_(c1, c3.startIndex)

// The underlying index types are different. What do you do (trap/nocompile)?
_ = subscript_(c1, c4.startIndex)

--
Dave

Is it sufficient to say that unconstrained associated types cannot satisfy generic constraints so subscript_ can’t be called with startIndex?
Presumably if you said Any<Collection where Element == Int, Index == Int> you could then call subscript_.

Presumably, if a type was of existential type but all its associated types were constrained to concrete types and none contain Self requirements, you should be able to use it to satisfy a generic parameter. (Self requirements are still a problem. `Equatable` has no associated types, but you'd still run into problems using that existential in a generic context.)

Practically speaking, this wouldn't be too useful for most complex protocols. Swift 3's `Collection` defines at least `SubSequence`, `Indices`, `Index` (I think), and `Iterator`, all of which would have to be bound to meet this requirement. Then you get into recursive associated type constraints...

Not being a compiler expert another thought occurs to me: Can the type of an existential/unconstrained associated type be an unique anonymous type in the local scope? In this case c1.startIndex is of type a’; it cannot be stored in a global, passed to another function, etc. It is only usable in the current scope for interacting with c1. c2.startIndex would be of type b’, etc. This is similar to how anonymous types work in C#. The issues with func<X>(a: X, b: X) could in theory be solved the same way: require opening the existential in the local scope and validating that the actual types are identical before you can call func.

Joe Groff thinks it's possible. I wrote something up along those lines here: https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure

···

On Jun 8, 2016, at 11:52 PM, Russ Bishop <xenadu@gmail.com> wrote:

On Jun 8, 2016, at 3:18 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

In that sense you could say the types are really:

let c1: a’<IntCollection>
let c2: b’<IntCollection>
let c3: c’<IntCollection>
let c4: d’<IntCollection>

// accepts responsibility for a and b being different concrete types at runtime
func doIt(a: IntCollection, b: IntCollection) { }

// caller must prove a & b are same concrete type
func makeIt<C: Collection>(a: C, b: C) { }

They are completely separate types until proven otherwise and you can’t go passing them around unless the callee accepts responsibility for the dynamism or you prove exactly which type you’re talking about.

(I freely admit this may be nonsensical or impossible to implement reasonably in the compiler)

Russ

Yes, you were correct. Thanks for spotting that and apologies for not
acknowledging you sooner.

Austin

···

On Thu, Jun 9, 2016 at 1:13 AM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:

On Jun 9, 2016, at 8:06 AM, Austin Zheng <austinzheng@gmail.com> wrote:

According to that code (which is in Decl.cpp), AnyObject (and Any)
conforms to itself; no non-@objc protocol is allowed to conform to
itself. An @objc protocol may or may not conform to itself, depending on
some other conditions.

Like I said... more sub-cases than your original statement, right?!

On Jun 8, 2016, at 9:19 PM, L. Mihalkovic <laurent.mihalkovic@gmail.com> > wrote:

On Jun 8, 2016, at 9:43 PM, Austin Zheng via swift-evolution < > swift-evolution@swift.org> wrote:

It's not possible, even with Swift's current implementation of
existentials. A protocol type P isn't considered to conform to itself, thus
the following is rejected:

The rules are not that simple, I seem to recall reading in sourcecode that
there are criteria that decide if that is true or not. And by default the
variable conform_to_self is even TRUE

let a : MyProtocol = // ...
func myFunc<T : MyProtocol>(x: T) {
  // ....
}
myFunc(a) // "Cannot invoke 'myFunc' with an argument list of type
MyProtocol"

Changing how this works is probably worth a proposal by itself.

Austin

On Wed, Jun 8, 2016 at 12:34 PM, Thorsten Seitz via swift-evolution < > swift-evolution@swift.org> wrote:

> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution < >> swift-evolution@swift.org>:
>
>
> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com >> <http://matthew-at-anandabits.com/&gt;&gt; wrote:
>
>>> On Jun 7, 2016, at 9:15 PM, Dave Abrahams <dabrahams@apple.com> >> wrote:
>>>
>>>
>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com >> <http://matthew-at-anandabits.com/&gt; <http://matthew-at-anandabits.com/&gt;&gt; >> wrote:
>>>
>>
>>>>> On Jun 7, 2016, at 4:13 PM, Dave Abrahams via swift-evolution < >> swift-evolution@swift.org> wrote:
>>>>>
>>>>>
>>>>> on Tue Jun 07 2016, Matthew Johnson <swift-evolution@swift.org> >> wrote:
>>>>>
>>>>
>>>>>>> , but haven't realized
>>>>>>> that if you step around the type relationships encoded in Self
>>>>>>> requirements and associated types you end up with types that
appear to
>>>>>>> interoperate but in fact trap at runtime unless used in exactly
the
>>>>>>> right way.
>>>>>>
>>>>>> Trap at runtime? How so? Generalized existentials should still be
>>>>>> type-safe.
>>>>>
>>>>> There are two choices when you erase static type relationships:
>>>>>
>>>>> 1. Acheive type-safety by trapping at runtime
>>>>>
>>>>> FloatingPoint(3.0 as Float) + FloatingPoint(3.0 as Double) // trap
>>>>>
>>>>> 2. Don't expose protocol requirements that involve these
relationships,
>>>>> which would prevent the code above from compiling and prevent
>>>>> FloatingPoint from conforming to itself.
>>>>>
>>>>>> Or are you talking about the hypothetical types / behaviors people
>>>>>> think they want when they don’t fully understand what is
happening...
>>>>>
>>>>> I don't know what you mean here. I think generalized existentials
will
>>>>> be nice to have, but I think most people will want them to do
something
>>>>> they can't possibly do.
>>>>
>>>> Exactly. What I meant is that people think they want that expression
>>>> to compile because they don’t understand that the only thing it can
do
>>>> is trap. I said “hypothetical” because producing a compile time
error
>>>> rather than a runtime trap is the only sane thing to do. Your
comment
>>>> surprised me because I can’t imagine we would move forward in Swift
>>>> with the approach of trapping.
>>>
>>> I would very much like to be able to create instances of “Collection
>>> where Element == Int” so we can throw away the wrappers in the stdlib.
>>> That will require some type mismatches to be caught at runtime via
>>> trapping.
>>
>> For invalid index because the existential accepts a type erased index?
>
> Exactly.
>
>> How do you decide where to draw the line here? It feels like a very
>> slippery slope for a language where safety is a stated priority to
>> start adopting a strategy of runtime trapping for something as
>> fundamental as how you expose members on an existential.
>
> If you don't do this, the alternative is that “Collection where Element
> == Int” does not conform to Collection. That's weird and not very
> useful. You could expose all the methods that were on protocol
> extensions of Collection on this existential, unless they used
> associated types other than the element type. But you couldn't pass the
> existential to a generic function like
>
> func scrambled<C: Collection>(_ c: C) -> [C.Element]

I don’t understand. Why couldn’t an existential be passed to that
function?

-Thorsten

>
>> IMO you should *have* to introduce unsafe behavior like that manually.
>
> Collection where Element == Int & Index == *
>
> ?
>
>> Collection indices are already something that isn’t fully statically
>> safe so I understand why you might want to allow this.
>
> By the same measure, so are Ints :-)
>
> The fact that a type's methods have preconditions does *not* make it
> “statically unsafe.”
>
>> But I don’t think having the language's existentials do this
>> automatically is the right approach. Maybe there is another approach
>> that could be used in targeted use cases where the less safe behavior
>> makes sense and is carefully designed.
>
> Whether it makes sense or not really depends on the use-cases. There's
> little point in generalizing existentials if the result isn't very
useful.
> The way to find out is to take a look at the examples we currently have
> of protocols with associated types or Self requirements and consider
> what you'd be able to do with their existentials if type relationships
> couldn't be erased.
>
> We have known use-cases, currently emulated in the standard library, for
> existentials with erased type relationships. *If* these represent the
> predominant use cases for something like generalized existentials, it
> seems to me that the language feature should support that. Note: I have
> not seen anyone build an emulation of the other kind of generalized
> existential. My theory: there's a good reason for that :-).
>
> --
> Dave
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

, but haven't realized
that if you step around the type relationships encoded in Self
requirements and associated types you end up with types that appear to
interoperate but in fact trap at runtime unless used in exactly the
right way.

Trap at runtime? How so? Generalized existentials should still be
type-safe.

There are two choices when you erase static type relationships:

1. Acheive type-safety by trapping at runtime

FloatingPoint(3.0 as Float) + FloatingPoint(3.0 as Double) // trap

2. Don't expose protocol requirements that involve these relationships,
which would prevent the code above from compiling and prevent
FloatingPoint from conforming to itself.

Or are you talking about the hypothetical types / behaviors people
think they want when they don’t fully understand what is happening...

I don't know what you mean here. I think generalized existentials will
be nice to have, but I think most people will want them to do something
they can't possibly do.

Exactly. What I meant is that people think they want that expression
to compile because they don’t understand that the only thing it can do
is trap. I said “hypothetical” because producing a compile time error
rather than a runtime trap is the only sane thing to do. Your comment
surprised me because I can’t imagine we would move forward in Swift
with the approach of trapping.

I would very much like to be able to create instances of “Collection
where Element == Int” so we can throw away the wrappers in the stdlib.
That will require some type mismatches to be caught at runtime via
trapping.

For invalid index because the existential accepts a type erased index?

Exactly.

How do you decide where to draw the line here? It feels like a very
slippery slope for a language where safety is a stated priority to
start adopting a strategy of runtime trapping for something as
fundamental as how you expose members on an existential.

If you don't do this, the alternative is that “Collection where Element
== Int” does not conform to Collection.

This isn’t directly related to having self or associated type
requirements. It is true of all existentials.

That is just an implementation limitation today, IIUC. What I'm talking
about here would make it impossible for some to do that.

If it is just an implementation limitation I am happy to hear that.

If that changes for simple existentials and generalized existentials
expose all members (as in the latest draft of the proposal) maybe it
will be possible for all existentials to conform to their protocol.

Not without introducing runtime traps. See my “subscript function”
example.

That's weird and not very
useful. You could expose all the methods that were on protocol
extensions of Collection on this existential, unless they used
associated types other than the element type. But you couldn't pass the
existential to a generic function like

func scrambled<C: Collection>(_ c: C) -> [C.Element]

IMO you should *have* to introduce unsafe behavior like that manually.

Collection where Element == Int & Index == *

?

I didn’t mean directly through the type of the existential.

My question is, why not? That is still explicit.

It’s not explicit in the sense that nobody wrote `fatalError` or
similar in their code. It’s too easy to write something like that
without realizing that it introduces the possibility of a crash. If
we adopt syntax like that to introduce an existential that introduces
traps we should at least require members that can be trap to be
invoked using a `!` suffix or something like that to make it clear to
users that a trap will happen if they are not extremely careful when
using that member.

More generally though, I don’t want the rules of the language to be
written in a way that causes the compiler to synthesize traps in such
a general way.

The existential should not introduce a precondition that isn’t already
present in the semantics of the protocol itself. If the semantics of
the protocol do not place preconditions on arguments beyond their type
(such as “must be a valid index into this specific instance”) the
compiler should not allow the existential to conform if a trap is
required in some circumstances. That is a new precondition and
therefore the existential does not actually fulfill the requirements
of the protocol.

I could *maybe* live with a solution where protocol requirements are
marked as trapping, etc depending on the specific argument received at
runtime. This is a total straw man syntax, but maybe `IndexableBase`
would declare the subscript `@trapping` (probably something different
but I hope this communicates the idea). This alerts users to the fact
that they need to be extra careful - not any value of `Self.Index` is
valid and you can get a crash if you’re not careful.

Having this semantic explicit in the definition of the protocol opens
the door to maybe considering an existential synthesized by the
compiler that traps because it doesn’t introduce a new precondition
that wasn’t already present in the protocol.

I would want to give consideration to specific details of a proposal
along these lines before deciding how I feel about it, but I have a
more open mind to this approach than introducing traps not present in
the preconditions of the protocol.

/// You can subscript a collection with any valid index other than the
/// collection's end index. The end index refers to the position one past
/// the last element of a collection, so it doesn't correspond with an
/// element.
///
/// - Parameter position: The position of the element to access. `position`
/// must be a valid index of the collection that is not equal to the
/// `endIndex` property.
@trapping public subscript(position: Self.Index) -> Self._Element { get }

One obvious mechanism for introducing unsafe behavior is to write
manual type erasure wrappers like we do today.

Another possibility would be to allow extending the existential type
(not the protocol). This would allow you to write overloads on the
Collection existential that takes some kind of type erased index if
that is what you want and either trap if you receive an invalid index
or better (IMO) return an `Element?`. I’m not sure how extensions on
existentials might be implemented, but this is an example of the kind
of operation you might want available on it that you wouldn’t want
available on all Collection types.

Collection indices are already something that isn’t fully statically
safe so I understand why you might want to allow this.

By the same measure, so are Ints :-)

The fact that a type's methods have preconditions does *not* make it
“statically unsafe.”

That depends on what you mean by safe. Sure, those methods aren’t
going corrupt memory, but they *are* going to explicitly and
intentionally crash for some inputs. That doesn’t qualify as “fully
safe” IMO.

Please pick a term other than “unsafe” here; it's not unsafe in the
sense we mean the word in Swift. It's safe in exactly the same way that
array indexes and integers are. When you violate a precondition, it
traps.

I am happy to use any word you like here.

Can you clarify what you mean by the word safe in Swift? It doesn’t
appear to be limited to memory safety in the public about page
https://swift.org/about/:

I mean memory- and type-safe.

Safe. The most obvious way to write code should also behave in a safe
manner. Undefined behavior is the enemy of safety, and developer
mistakes should be caught before software is in production. Opting for
safety sometimes means Swift will feel strict, but we believe that
clarity saves time in the long run.

Safety

Swift was designed from the outset to be safer than C-based languages,
and eliminates entire classes of unsafe code. Variables are always
initialized before use, arrays and integers are checked for overflow,
and memory is managed automatically. Syntax is tuned to make it easy
to define your intent — for example, simple three-character keywords
define a variable (var) or constant (let).

Another safety feature is that by default Swift objects can never be
nil, and trying to make or use a nil object will results in a
compile-time error. This makes writing code much cleaner and safer,
and prevents a common cause of runtime crashes. However, there are
cases where nil is appropriate, and for these situations Swift has an
innovative feature known as optionals. An optional may contain nil,
but Swift syntax forces you to safely deal with it using ? to indicate
to the compiler you understand the behavior and will handle it safely.

This positioning statement makes it appear as if preventing common
causes of crashes falls within the meaning of safe that Swift is
using. Having existentials introduce new preconditions and traps when
they are not met does not seem aligned with that goal IMO.

Static typing “increases safety,” in the casual sense. That doesn't
mean that an operation that traps on a failed precondition check is
“unsafe.”

The user doesn't do anything “manual” to introduce that trapping
behavior for integers. Preconditions are a natural part of most types.

The user doesn’t, but isn’t the overflow trap implemented in the
standard library?

Whether it is or is not is an implementation detail.

Regardless, this is a specific case that has been given explicit
design attention by humans. The precondition is designed, not
introduced by compiler rules that haven’t considered the specific case
in question.

But I don’t think having the language's existentials do this
automatically is the right approach. Maybe there is another
approach that could be used in targeted use cases where the less
safe behavior makes sense and is carefully designed.

Whether it makes sense or not really depends on the use-cases. There's
little point in generalizing existentials if the result isn't very useful.

Usefulness depends on your perspective.

Of course. As I've said, let's look at the use cases.

Agree. We can consider those in depth when the time comes to ramp up
discussion of Austin’s proposal.

I have run into several scenarios where they would be very useful
without needing to be prone to crashes when used incorrectly. One
obvious basic use case is storing things in a heterogenous collection
where you bind .

bind what?

Sorry, I must have gotten distracted and not finished that paragraph.
I meant to say bind the associated types that are necessary for your
use case. Sometimes you bind *all* of the associated types to
concrete types and the protocol has no `Self` requirements. In that
case there is no trouble at all in conforming the type-erased
“existential" to the protocol itself. Austin’s proposal would
eliminate the need to manually write these “existentials” manually.

The way to find out is to take a look at the examples we currently have
of protocols with associated types or Self requirements and consider
what you'd be able to do with their existentials if type relationships
couldn't be erased.

We have known use-cases, currently emulated in the standard library, for
existentials with erased type relationships. *If* these represent the
predominant use cases for something like generalized existentials, it
seems to me that the language feature should support that. Note: I have
not seen anyone build an emulation of the other kind of generalized
existential. My theory: there's a good reason for that :-).

AFAIK (and I could be wrong) the only rules in the language that
require the compiler to synthesize a trap except using a nil IUO, `!`
on a nil Optional, and an invalid `as` cast . These are all
syntactically explicit unsafe / dangerous operations. All other traps
are in the standard library (array index, overflow, etc). Most
important about all of these cases is that they have received direct
human consideration.

There is no distinction in the user model between what might be
synthesized by the language and what appears on standard library types.

Maybe I shouldn’t have made that distinction.

The point I am trying to emphasize is that each of these are special
cases that have received direct human consideration. The potential
for a trap is not introduced by language rules that apply to
user-defined constructs in without consideration of the specific
details of that construct.

Introducing a language (not library) mechanism that exposes members on
generalized existentials in a way that relies on runtime traps for
type safety feels to me like a pretty dramatic turn agains the stated
priority of safety. It will mean you must understand exactly what is
going on and be extremely careful to use generalized existentials
without causing crashes. This will either make Swift code much more
crashy or will scare people away from using generalized existentials
(and maybe both).

I don't accept either of those statements without seeing some analysis
of the use-cases. For example, I don't believe that AnyCollection et al
are particularly crash-prone. The likelihood that you'll use the wrong
index type with a collection is very, very low. I'm less certain of
what happens with Self requirements in real cases.

But again, I believe this is an exceptional case as the precondition
is explicitly stated in the semantics of the protocol.

IIUC, it has been cited by Doug as the exemplar of the
predominantly-requested case by a 10:1 ratio!

IMO the burden of proof should be on the side that proposes a
mechanism to introduce traps, not the side that proposes avoiding
them.

If you really want to make this about sides and burdens, the burden of
proof always rests with the side proposing to extend the language. We
shouldn't be making changes without understanding how they will play out
in real use-cases.

Neither of those outcomes is good.

Collection indices are a somewhat special case as there is already a
strong precondition that people are familiar with because it would be
too costly to performance and arguably too annoying to deal with an
Optional result in every array lookup. IMO that is why the library is
able to get away with it in the current type erased AnyCollection.
But this is not a good model for exposing any members on an
existential that do not already have a strong precondition that causes
a trap when violated.

I think a big reason why you maybe haven’t seen a lot of examples of
people writing type erased “existentials" is because it is a huge pain
in the neck to write this stuff manually today. People may be
designing around the need for them. I haven’t seen a huge sampling of
type erased “existentials" other people are writing but I haven’t
written any that introduce a trap like this. The only traps are in
the “abstract" base class whose methods will never be called (and
wouldn’t even be implemented if they could be marked abstract).

What specific things do you think we need to be able to do that rely
on the compiler synthesizing a trap in the way it exposes the members
of the existential?

I don't know. I'm saying, I don't think we understand the use-cases
well enough to make a determination.

That’s fair. I agree that use cases should be carefully considered.

Here are a few examples from Austin’s proposal that safely use
existential collections. I don’t understand why you think this
approach is insufficient. Maybe you could supply a concrete example
of a use case that can’t be written with the mechanism in Austin’s
proposal.

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure <https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure&gt;

let a : Any<Collection>

// A variable whose type is the Index associated type of the underlying
// concrete type of 'a'.
let theIndex : a.Index = ...

// A variable whose type is the Element associated type of the underlying
// concrete type of 'a'.
let theElement : a.Element = ...

// Given a mutable collection, swap its first and last items.
// Not a generic function.
func swapFirstAndLast(inout collection: Any<BidirectionalMutableCollection>) {
   // firstIndex and lastIndex both have type "collection.Index"
   guard let firstIndex = collection.startIndex,
       lastIndex = collection.endIndex?.predecessor(collection) where lastIndex != firstIndex else {
           print("Nothing to do")
           return
   }

   // oldFirstItem has type "collection.Element"
   let oldFirstItem = collection[firstIndex]

   collection[firstIndex] = collection[lastIndex]
   collection[lastIndex] = oldFirstItem
}

var a : Any<BidirectionalMutableCollection where .Element == String> = ...

let input = "West Meoley"

// Not actually necessary, since the compiler knows "a.Element" is String.
// A fully constrained anonymous associated type is synonymous with the concrete
// type it's forced to take on, and the two are interchangeable.
// However, 'as' casting is still available if desired.
let anonymousInput = input as a.Element

a[a.startIndex] = anonymousInput

// as mentioned, this also works:
a[a.startIndex] = input

// If the collection allows it, set the first element in the collection to a given string.
func setFirstElementIn(inout collection: Any<Collection> toString string: String) {
   if let element = string as? collection.Element {
       // At this point, 'element' is of type "collection.Element"
       collection[collection.startIndex] = element
   }
}

Neither of these look like they actually make *use* of the fact that
there's type erasure involved (and therefore should probably be written
as generics?). The interesting cases with Any<Collection...>, for the
purposes of this discussion, arise when you have multiple instances of
the same existential type that wrap different concrete types.

One use case I have found is to work around the lack of higher-kinder
types.

Really, now: a use-case for feature A that is a workaround for the lack
of feature B hardly justifies adding feature A! We do want to add
higher-kinded types eventually.

If you have a protocol where specific implementations will return
different types, but all conform to a second protocol you can define
the protocol in terms of a generic type-erased wrapper which conforms
to the second protocol and accepts type arguments that match the
associated types (thus binding the associated types to concrete
types). I have found this to be a useful technique (granted it is a
workaround and I’m not sure how useful it would continue to be if
Swift eventually gets higher-kinder types).

Another problem I see: in this new world, what is the model for choosing
whether to write a function as a protocol extension/generic, or as a
regular function taking existential parameters? Given that either of
the above could have been written either way, we need to be able to
answer that question. When existentials don't conform to their
protocols, it seems to me that the most general thing to do is use
existentials whenever you can, and only resort to using generics when
forced by the type system. This does not seem like a particularly good
programming model to me, but I might be convinced otherwise.

That doesn’t seem like a particularly good programming model to me either.

The rule of thumb I am operating with for protocols with Self or
associated type requirements is to prefer generics and use type
erasure / existentials when that isn’t possible. For example, when
heterogeneity is required or when you can’t form the necessary type in
a protocol requirement (as in the preceding example).

This heuristic has been working out pretty well for me thus far.

I do worry a bit that people will choose the opposite heuristic.

It would be somewhat reassuring to me if we could prove to ourselves
that, using your heuristic, one is never forced to copy/paste a generic
function implementation into a corresponding function that uses
existentials.

The primary impact of introducing a language mechanism for generalized
existentials in my code would be to eliminate a lot of manual type
erasing boilerplate.

If your code has many manual type erasing wrappers corresponding to
protocols with associated types and/or Self requirements that also never
have to trap type mismatches, that would certainly be instructive
empirical data. Would you care to share the protocols and wrappers you
are talking about?

···

on Thu Jun 09 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:

On Jun 9, 2016, at 9:55 AM, Dave Abrahams <dabrahams@apple.com> wrote:
on Wed Jun 08 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 8, 2016, at 1:33 PM, Dave Abrahams <dabrahams@apple.com> wrote:
on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:

On Jun 7, 2016, at 9:15 PM, Dave Abrahams <dabrahams@apple.com> wrote:
on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 7, 2016, at 4:13 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Tue Jun 07 2016, Matthew Johnson <swift-evolution@swift.org> wrote:

Anyway, my overall point is that this all seems like something we *can*
do and that nicely fills gaps in the type system, but not necessarily
something we *should* do until we better understand what it's actually
*for* and how it affects the programming model.

That’s a very fair position to take. :)

--
Dave

--
Dave

>>
>>> Introducing a language (not library) mechanism that exposes members on
>>> generalized existentials in a way that relies on runtime traps for
>>> type safety feels to me like a pretty dramatic turn agains the stated
>>> priority of safety. It will mean you must understand exactly what is
>>> going on and be extremely careful to use generalized existentials
>>> without causing crashes. This will either make Swift code much more
>>> crashy or will scare people away from using generalized existentials
>>> (and maybe both).
>>
>> I don't accept either of those statements without seeing some analysis
>> of the use-cases. For example, I don't believe that AnyCollection et al
>> are particularly crash-prone. The likelihood that you'll use the wrong
>> index type with a collection is very, very low. I'm less certain of
>> what happens with Self requirements in real cases.
>
> But again, I believe this is an exceptional case as the precondition
> is explicitly stated in the semantics of the protocol.

IIUC, it has been cited by Doug as the exemplar of the
predominantly-requested case by a 10:1 ratio!

At the risk of sounding waffly, I see both sides of the argument.

Java is typesafe in the formal sense (can't perform an operation on a type
that doesn't actually support it); almost certainly moreso than Swift
(which lets you do wonderful things like reinterpret casting, C-style
direct memory manipulation...).

However, in many cases Swift provides better compile-time assurances than
Java. In Java, any reference might actually be a null reference waiting to
cause a NullPointerException unless you explicitly check it in code (and
what if you forget to check it somewhere?). In Swift, (aside from IUOs and
a few other features) if your program compiles successfully the type
checker has proven that your software will never try calling a method upon
nil. And if you do use IUOs the postfix exclamation marks clearly mark
spots in your code that might cause trouble.

In the same way I don't think trapping at runtime if an existential
satisfying a generic type parameter causes a type error is the best
solution to this problem. What we are doing is allowing a type that makes a
weak guarantee to stand in for a type that makes a strong guarantee, and
catching mismatches between those guarantees if/when they pop up. There's
no easy way for someone perusing the code to tell that such a mismatch
might occur, equivalent to IUO's postfix !.

What I prefer is to explicitly "promote" the type with the weak guarantee
to a type with the stronger guarantee using the existential opening
mechanism that we've been throwing around. That way, it's very clear what
is going on: if the existentials in question can't serve as generic
parameters to the function you want to pass them to, you can fatalError()
or do something else. Or you can try force-opening with `as!` and mark a
potential trouble spot in the same way an IUO does. Once it succeeds you
know you have the strong guarantees that will cause your generic member to
alway work the way it should.

With the proposed mechanism, I expect that Any<Collection where .Element ==

should be less prone to runtime trapping than AnyCollection<T>, because

we can reify the concept that each instance of the existential has a
concrete Index type associated with it, rather than relying upon a
type-erased AnyIndex shared among all AnyCollections. (That being said, I
don't know if AnyCollection is particularly problematic in practical use. I
also don't know if other type-erased wrappers users might define might be
more or less prone to this type of issue.)

> IMO the burden of proof should be on the side that proposes a
> mechanism to introduce traps, not the side that proposes avoiding
> them.

If you really want to make this about sides and burdens, the burden of
proof always rests with the side proposing to extend the language. We
shouldn't be making changes without understanding how they will play out
in real use-cases.

Yes, I really want to see real-world use cases.

>>
>> Neither of these look like they actually make *use* of the fact that
>> there's type erasure involved (and therefore should probably be written
>> as generics?). The interesting cases with Any<Collection...>, for the
>> purposes of this discussion, arise when you have multiple instances of
>> the same existential type that wrap different concrete types.
>
> One use case I have found is to work around the lack of higher-kinder
> types.

Really, now: a use-case for feature A that is a workaround for the lack
of feature B hardly justifies adding feature A! We do want to add
higher-kinded types eventually.

I just want to say, this made my day.

Most of the examples in the document Matthew linked were meant to be
didactic, not practical. I would love to swap them out for more useful
ones.

>>
>> Another problem I see: in this new world, what is the model for choosing
>> whether to write a function as a protocol extension/generic, or as a
>> regular function taking existential parameters? Given that either of
>> the above could have been written either way, we need to be able to
>> answer that question. When existentials don't conform to their
>> protocols, it seems to me that the most general thing to do is use
>> existentials whenever you can, and only resort to using generics when
>> forced by the type system. This does not seem like a particularly good
>> programming model to me, but I might be convinced otherwise.
>
> That doesn’t seem like a particularly good programming model to me
either.
>
> The rule of thumb I am operating with for protocols with Self or
> associated type requirements is to prefer generics and use type
> erasure / existentials when that isn’t possible. For example, when
> heterogeneity is required or when you can’t form the necessary type in
> a protocol requirement (as in the preceding example).
>
> This heuristic has been working out pretty well for me thus far.

I do worry a bit that people will choose the opposite heuristic.

If we are to introduce this feature, I expect that the community would go
through another "reference vs value types - which is right for you?" type
discussion. It's true you can model a lot of things with classes where
structs would be more appropriate, but this doesn't seem to be a big issue.

I would be very interested in affordances that can steer people towards
using generics when the dynamic nature of existentials isn't necessary.

···

On Thu, Jun 9, 2016 at 9:42 AM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Thu Jun 09 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:

______________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Part of the problem is you get used to living inside the cell and forget what life might be like on the outside. I don’t run in to many cases anymore because I know they won’t work and pre-emotively constrain my designs to fit into the existing restrictions.

Sorry for the long bit of code, I tried to boil it down to the absolute minimum. It has the advantage of being actual shipping code:

public protocol AnyMutationOperation {
    func __mutate(object: Any) -> Any
}

public protocol MutationOperation: AnyMutationOperation {
    associatedtype ObjectType: MutatableSyncable
    func _mutate(object: ObjectType) -> ObjectType
}

public protocol MutatableSyncable {
    var _operations: [AnyMutationOperation] { get set }
    mutating func mutate<T: MutationOperation where T.ObjectType == Self>(mutation: T) -> Self
}

extension MutatableSyncable {
    mutating func mutate<T: MutationOperation where T.ObjectType == Self>(mutation: T) -> Self {
        self._operations.append(mutation)
        return mutation._mutate(self)
    }
    mutating func _mutate(mutation: AnyMutationOperation) -> Self {
        return mutation.__mutate(self) as! Self
    }
}

extension MutationOperation {
    func __mutate(object: Any) -> Any {
        return self._mutate(object as! ObjectType)
    }
}

struct Model: MutatableSyncable {
    var _operations: [AnyMutationOperation] =
    var name: String = ""
}

struct ChangeNameOperation: MutationOperation {
    var newName: String
    func _mutate(object: Model) -> Model {
        var copy = object
        copy.name = self.newName
        return object
    }
}

AnyMutationOperation, the force casting, etc are all to work around the inability to call MutatableSyncable.mutate() with MutationOperations that came from the database.

// at local point of mutation, works fine
var model = Model()
let op = ChangeNameOperation(newName: "bob")
model.mutate(op)

// when trying to rollback/rollforward in sync manager, which handles many model and mutation types
var model: MutatableSyncable = …
// this can't even be MutationOperation
let op: AnyMutationOperation = …

// ERROR, can't do that!
//model.mutate(op)

// Better hope none of those force casts trap!
model._mutate(op)

We didn’t want to encode a massive list of 100s of types in a big ol’ switch statement because we’re modeling every single mutable field as a separate MutationOperation. All we know in the sync manager is we have a MutatableSyncable and some MutationOperation (but in our type-erased AnyMutationOperation). So we have a fragile system that traps if anyone makes a mistake. I’d much rather be able to unwrap the existential and validate that the operation’s ObjectType matches the model’s concrete type, then call the generic version of mutate().

There is another place where we want to find the ObjectManager<T: ModelType> for the model type T, then call it but we run into the same associated types problem. We couldn’t create a Dictionary<ModelType, ObjectManager<ModelType>> because ObjectManager has associated types.

In that case the list of model types was constrained enough we went for the big switch statement but it takes Any so is completely unchecked by the compiler even though we know that we could constrain it to be type safe:

func saveFromServer(model: Any) throws {
    switch model {
    case let model as PGReportTypeFullModel:
        try self.reportTypeManager.saveFromServer(model)
    // lots of others here ...
    }
}

I’ll see if I can find some more examples later.

Russ

···

On Jun 9, 2016, at 9:42 AM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

If your code has many manual type erasing wrappers corresponding to
protocols with associated types and/or Self requirements that also never
have to trap type mismatches, that would certainly be instructive
empirical data. Would you care to share the protocols and wrappers you
are talking about?

--
Dave

, but haven't realized
that if you step around the type relationships encoded in Self
requirements and associated types you end up with types that appear to
interoperate but in fact trap at runtime unless used in exactly the
right way.

Trap at runtime? How so? Generalized existentials should still be
type-safe.

There are two choices when you erase static type relationships:

1. Acheive type-safety by trapping at runtime

FloatingPoint(3.0 as Float) + FloatingPoint(3.0 as Double) // trap

2. Don't expose protocol requirements that involve these relationships,
which would prevent the code above from compiling and prevent
FloatingPoint from conforming to itself.

Or are you talking about the hypothetical types / behaviors people
think they want when they don’t fully understand what is happening...

I don't know what you mean here. I think generalized existentials will
be nice to have, but I think most people will want them to do something
they can't possibly do.

Exactly. What I meant is that people think they want that expression
to compile because they don’t understand that the only thing it can do
is trap. I said “hypothetical” because producing a compile time error
rather than a runtime trap is the only sane thing to do. Your comment
surprised me because I can’t imagine we would move forward in Swift
with the approach of trapping.

I would very much like to be able to create instances of “Collection
where Element == Int” so we can throw away the wrappers in the stdlib.
That will require some type mismatches to be caught at runtime via
trapping.

For invalid index because the existential accepts a type erased index?

Exactly.

How do you decide where to draw the line here? It feels like a very
slippery slope for a language where safety is a stated priority to
start adopting a strategy of runtime trapping for something as
fundamental as how you expose members on an existential.

If you don't do this, the alternative is that “Collection where Element
== Int” does not conform to Collection.

This isn’t directly related to having self or associated type
requirements. It is true of all existentials.

That is just an implementation limitation today, IIUC. What I'm talking
about here would make it impossible for some to do that.

If it is just an implementation limitation I am happy to hear that.

If that changes for simple existentials and generalized existentials
expose all members (as in the latest draft of the proposal) maybe it
will be possible for all existentials to conform to their protocol.

Not without introducing runtime traps. See my “subscript function”
example.

That's weird and not very
useful. You could expose all the methods that were on protocol
extensions of Collection on this existential, unless they used
associated types other than the element type. But you couldn't pass the
existential to a generic function like

func scrambled<C: Collection>(_ c: C) -> [C.Element]

IMO you should *have* to introduce unsafe behavior like that manually.

Collection where Element == Int & Index == *

?

I didn’t mean directly through the type of the existential.

My question is, why not? That is still explicit.

It’s not explicit in the sense that nobody wrote `fatalError` or
similar in their code. It’s too easy to write something like that
without realizing that it introduces the possibility of a crash. If
we adopt syntax like that to introduce an existential that introduces
traps we should at least require members that can be trap to be
invoked using a `!` suffix or something like that to make it clear to
users that a trap will happen if they are not extremely careful when
using that member.

More generally though, I don’t want the rules of the language to be
written in a way that causes the compiler to synthesize traps in such
a general way.

The existential should not introduce a precondition that isn’t already
present in the semantics of the protocol itself. If the semantics of
the protocol do not place preconditions on arguments beyond their type
(such as “must be a valid index into this specific instance”) the
compiler should not allow the existential to conform if a trap is
required in some circumstances. That is a new precondition and
therefore the existential does not actually fulfill the requirements
of the protocol.

I could *maybe* live with a solution where protocol requirements are
marked as trapping, etc depending on the specific argument received at
runtime. This is a total straw man syntax, but maybe `IndexableBase`
would declare the subscript `@trapping` (probably something different
but I hope this communicates the idea). This alerts users to the fact
that they need to be extra careful - not any value of `Self.Index` is
valid and you can get a crash if you’re not careful.

Having this semantic explicit in the definition of the protocol opens
the door to maybe considering an existential synthesized by the
compiler that traps because it doesn’t introduce a new precondition
that wasn’t already present in the protocol.

I would want to give consideration to specific details of a proposal
along these lines before deciding how I feel about it, but I have a
more open mind to this approach than introducing traps not present in
the preconditions of the protocol.

/// You can subscript a collection with any valid index other than the
/// collection's end index. The end index refers to the position one past
/// the last element of a collection, so it doesn't correspond with an
/// element.
///
/// - Parameter position: The position of the element to access. `position`
/// must be a valid index of the collection that is not equal to the
/// `endIndex` property.
@trapping public subscript(position: Self.Index) -> Self._Element { get }

One obvious mechanism for introducing unsafe behavior is to write
manual type erasure wrappers like we do today.

Another possibility would be to allow extending the existential type
(not the protocol). This would allow you to write overloads on the
Collection existential that takes some kind of type erased index if
that is what you want and either trap if you receive an invalid index
or better (IMO) return an `Element?`. I’m not sure how extensions on
existentials might be implemented, but this is an example of the kind
of operation you might want available on it that you wouldn’t want
available on all Collection types.

Collection indices are already something that isn’t fully statically
safe so I understand why you might want to allow this.

By the same measure, so are Ints :-)

The fact that a type's methods have preconditions does *not* make it
“statically unsafe.”

That depends on what you mean by safe. Sure, those methods aren’t
going corrupt memory, but they *are* going to explicitly and
intentionally crash for some inputs. That doesn’t qualify as “fully
safe” IMO.

Please pick a term other than “unsafe” here; it's not unsafe in the
sense we mean the word in Swift. It's safe in exactly the same way that
array indexes and integers are. When you violate a precondition, it
traps.

I am happy to use any word you like here.

Can you clarify what you mean by the word safe in Swift? It doesn’t
appear to be limited to memory safety in the public about page
Swift.org - About Swift <https://swift.org/about/&gt;:

I mean memory- and type-safe.

Safe. The most obvious way to write code should also behave in a safe
manner. Undefined behavior is the enemy of safety, and developer
mistakes should be caught before software is in production. Opting for
safety sometimes means Swift will feel strict, but we believe that
clarity saves time in the long run.

Safety

Swift was designed from the outset to be safer than C-based languages,
and eliminates entire classes of unsafe code. Variables are always
initialized before use, arrays and integers are checked for overflow,
and memory is managed automatically. Syntax is tuned to make it easy
to define your intent — for example, simple three-character keywords
define a variable (var) or constant (let).

Another safety feature is that by default Swift objects can never be
nil, and trying to make or use a nil object will results in a
compile-time error. This makes writing code much cleaner and safer,
and prevents a common cause of runtime crashes. However, there are
cases where nil is appropriate, and for these situations Swift has an
innovative feature known as optionals. An optional may contain nil,
but Swift syntax forces you to safely deal with it using ? to indicate
to the compiler you understand the behavior and will handle it safely.

This positioning statement makes it appear as if preventing common
causes of crashes falls within the meaning of safe that Swift is
using. Having existentials introduce new preconditions and traps when
they are not met does not seem aligned with that goal IMO.

Static typing “increases safety,” in the casual sense. That doesn't
mean that an operation that traps on a failed precondition check is
“unsafe.”

The user doesn't do anything “manual” to introduce that trapping
behavior for integers. Preconditions are a natural part of most types.

The user doesn’t, but isn’t the overflow trap implemented in the
standard library?

Whether it is or is not is an implementation detail.

Regardless, this is a specific case that has been given explicit
design attention by humans. The precondition is designed, not
introduced by compiler rules that haven’t considered the specific case
in question.

But I don’t think having the language's existentials do this
automatically is the right approach. Maybe there is another
approach that could be used in targeted use cases where the less
safe behavior makes sense and is carefully designed.

Whether it makes sense or not really depends on the use-cases. There's
little point in generalizing existentials if the result isn't very useful.

Usefulness depends on your perspective.

Of course. As I've said, let's look at the use cases.

Agree. We can consider those in depth when the time comes to ramp up
discussion of Austin’s proposal.

I have run into several scenarios where they would be very useful
without needing to be prone to crashes when used incorrectly. One
obvious basic use case is storing things in a heterogenous collection
where you bind .

bind what?

Sorry, I must have gotten distracted and not finished that paragraph.
I meant to say bind the associated types that are necessary for your
use case. Sometimes you bind *all* of the associated types to
concrete types and the protocol has no `Self` requirements. In that
case there is no trouble at all in conforming the type-erased
“existential" to the protocol itself. Austin’s proposal would
eliminate the need to manually write these “existentials” manually.

The way to find out is to take a look at the examples we currently have
of protocols with associated types or Self requirements and consider
what you'd be able to do with their existentials if type relationships
couldn't be erased.

We have known use-cases, currently emulated in the standard library, for
existentials with erased type relationships. *If* these represent the
predominant use cases for something like generalized existentials, it
seems to me that the language feature should support that. Note: I have
not seen anyone build an emulation of the other kind of generalized
existential. My theory: there's a good reason for that :-).

AFAIK (and I could be wrong) the only rules in the language that
require the compiler to synthesize a trap except using a nil IUO, `!`
on a nil Optional, and an invalid `as` cast . These are all
syntactically explicit unsafe / dangerous operations. All other traps
are in the standard library (array index, overflow, etc). Most
important about all of these cases is that they have received direct
human consideration.

There is no distinction in the user model between what might be
synthesized by the language and what appears on standard library types.

Maybe I shouldn’t have made that distinction.

The point I am trying to emphasize is that each of these are special
cases that have received direct human consideration. The potential
for a trap is not introduced by language rules that apply to
user-defined constructs in without consideration of the specific
details of that construct.

Introducing a language (not library) mechanism that exposes members on
generalized existentials in a way that relies on runtime traps for
type safety feels to me like a pretty dramatic turn agains the stated
priority of safety. It will mean you must understand exactly what is
going on and be extremely careful to use generalized existentials
without causing crashes. This will either make Swift code much more
crashy or will scare people away from using generalized existentials
(and maybe both).

I don't accept either of those statements without seeing some analysis
of the use-cases. For example, I don't believe that AnyCollection et al
are particularly crash-prone. The likelihood that you'll use the wrong
index type with a collection is very, very low. I'm less certain of
what happens with Self requirements in real cases.

But again, I believe this is an exceptional case as the precondition
is explicitly stated in the semantics of the protocol.

IIUC, it has been cited by Doug as the exemplar of the
predominantly-requested case by a 10:1 ratio!

In terms of forming the existential, storing it in variables, accepting arguments of that type, etc yes. I don’t know how many of those requests expect it to conform to the protocol and expect to be able to use it in generic code constrained to the protocol.

IMO the burden of proof should be on the side that proposes a
mechanism to introduce traps, not the side that proposes avoiding
them.

If you really want to make this about sides and burdens, the burden of
proof always rests with the side proposing to extend the language. We
shouldn't be making changes without understanding how they will play out
in real use-cases.

I agree with this. But if we are discussing two different options for extending the language I think the option that doesn’t introduce crashes should be preferred without pretty compelling reasons to choose the option that can introduce crashes.

Neither of those outcomes is good.

Collection indices are a somewhat special case as there is already a
strong precondition that people are familiar with because it would be
too costly to performance and arguably too annoying to deal with an
Optional result in every array lookup. IMO that is why the library is
able to get away with it in the current type erased AnyCollection.
But this is not a good model for exposing any members on an
existential that do not already have a strong precondition that causes
a trap when violated.

I think a big reason why you maybe haven’t seen a lot of examples of
people writing type erased “existentials" is because it is a huge pain
in the neck to write this stuff manually today. People may be
designing around the need for them. I haven’t seen a huge sampling of
type erased “existentials" other people are writing but I haven’t
written any that introduce a trap like this. The only traps are in
the “abstract" base class whose methods will never be called (and
wouldn’t even be implemented if they could be marked abstract).

What specific things do you think we need to be able to do that rely
on the compiler synthesizing a trap in the way it exposes the members
of the existential?

I don't know. I'm saying, I don't think we understand the use-cases
well enough to make a determination.

That’s fair. I agree that use cases should be carefully considered.

Here are a few examples from Austin’s proposal that safely use
existential collections. I don’t understand why you think this
approach is insufficient. Maybe you could supply a concrete example
of a use case that can’t be written with the mechanism in Austin’s
proposal.

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure <https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure&gt; <https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure&lt;https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure&gt;&gt;

let a : Any<Collection>

// A variable whose type is the Index associated type of the underlying
// concrete type of 'a'.
let theIndex : a.Index = ...

// A variable whose type is the Element associated type of the underlying
// concrete type of 'a'.
let theElement : a.Element = ...

// Given a mutable collection, swap its first and last items.
// Not a generic function.
func swapFirstAndLast(inout collection: Any<BidirectionalMutableCollection>) {
  // firstIndex and lastIndex both have type "collection.Index"
  guard let firstIndex = collection.startIndex,
      lastIndex = collection.endIndex?.predecessor(collection) where lastIndex != firstIndex else {
          print("Nothing to do")
          return
  }

  // oldFirstItem has type "collection.Element"
  let oldFirstItem = collection[firstIndex]

  collection[firstIndex] = collection[lastIndex]
  collection[lastIndex] = oldFirstItem
}

var a : Any<BidirectionalMutableCollection where .Element == String> = ...

let input = "West Meoley"

// Not actually necessary, since the compiler knows "a.Element" is String.
// A fully constrained anonymous associated type is synonymous with the concrete
// type it's forced to take on, and the two are interchangeable.
// However, 'as' casting is still available if desired.
let anonymousInput = input as a.Element

a[a.startIndex] = anonymousInput

// as mentioned, this also works:
a[a.startIndex] = input

// If the collection allows it, set the first element in the collection to a given string.
func setFirstElementIn(inout collection: Any<Collection> toString string: String) {
  if let element = string as? collection.Element {
      // At this point, 'element' is of type "collection.Element"
      collection[collection.startIndex] = element
  }
}

Neither of these look like they actually make *use* of the fact that
there's type erasure involved (and therefore should probably be written
as generics?). The interesting cases with Any<Collection...>, for the
purposes of this discussion, arise when you have multiple instances of
the same existential type that wrap different concrete types.

One use case I have found is to work around the lack of higher-kinder
types.

Really, now: a use-case for feature A that is a workaround for the lack
of feature B hardly justifies adding feature A! We do want to add
higher-kinded types eventually.

Good to know. I thought higher-kinder types were on the “maybe if someone shows a compelling enough use case” list. AFAIK this is the first time a member of the core team has stated the intent to add them. If that is the case I agree that this use case isn’t relevant. The workaround isn’t great because it loses type information that is critical to the optimizer (but it’s all we have available today).

If you have a protocol where specific implementations will return
different types, but all conform to a second protocol you can define
the protocol in terms of a generic type-erased wrapper which conforms
to the second protocol and accepts type arguments that match the
associated types (thus binding the associated types to concrete
types). I have found this to be a useful technique (granted it is a
workaround and I’m not sure how useful it would continue to be if
Swift eventually gets higher-kinder types).

Another problem I see: in this new world, what is the model for choosing
whether to write a function as a protocol extension/generic, or as a
regular function taking existential parameters? Given that either of
the above could have been written either way, we need to be able to
answer that question. When existentials don't conform to their
protocols, it seems to me that the most general thing to do is use
existentials whenever you can, and only resort to using generics when
forced by the type system. This does not seem like a particularly good
programming model to me, but I might be convinced otherwise.

That doesn’t seem like a particularly good programming model to me either.

The rule of thumb I am operating with for protocols with Self or
associated type requirements is to prefer generics and use type
erasure / existentials when that isn’t possible. For example, when
heterogeneity is required or when you can’t form the necessary type in
a protocol requirement (as in the preceding example).

This heuristic has been working out pretty well for me thus far.

I do worry a bit that people will choose the opposite heuristic.

It would be somewhat reassuring to me if we could prove to ourselves
that, using your heuristic, one is never forced to copy/paste a generic
function implementation into a corresponding function that uses
existentials.

The primary impact of introducing a language mechanism for generalized
existentials in my code would be to eliminate a lot of manual type
erasing boilerplate.

If your code has many manual type erasing wrappers corresponding to
protocols with associated types and/or Self requirements that also never
have to trap type mismatches, that would certainly be instructive
empirical data. Would you care to share the protocols and wrappers you
are talking about?

I put together a sample implementation of a Cocoa-like responder chain in Swift a while ago when the “Swift dynamism” debate was raging.

It isn't intended to be a Swifty design. It is intended to be similar to Cocoa and show techniques that can be used to do things similar to Cocoa’s responder chain and targer-action in Swift. It uses a type erased wrapper for actions that binds `Sender` while hiding the concrete `Action` type and also the `Handler` associated type. It cannot and should not conform to the protocol it is derived from and could be replaced with the generalized existentials in Austin’s proposal.

This is a good example to start with because it is related to a topic that has been hotly debated and is clearly something a lot of people want to be able to do.

···

On Jun 9, 2016, at 11:42 AM, Dave Abrahams <dabrahams@apple.com> wrote:
on Thu Jun 09 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 9, 2016, at 9:55 AM, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@apple.com>> wrote:
on Wed Jun 08 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt; <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 8, 2016, at 1:33 PM, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@apple.com>> wrote:
on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 7, 2016, at 9:15 PM, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@apple.com>> wrote:
on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt; <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 7, 2016, at 4:13 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
on Tue Jun 07 2016, Matthew Johnson <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Anyway, my overall point is that this all seems like something we *can*
do and that nicely fills gaps in the type system, but not necessarily
something we *should* do until we better understand what it's actually
*for* and how it affects the programming model.

That’s a very fair position to take. :)

--
Dave

--
Dave

I think the problem is more that Any<Collection> does not conform to a specific value for a type parameter T: Collection

What I mean by this is that `Collection` denotes a type family, a generic parameter `T: Collection` denotes a specific (though unknown) member of that type family and `Any<Collection>` denotes the type family again, so there is really no point in writing Any<Collection> IMO.
The type family cannot conform to T because T is just one fixed member of it.
It conforms to itself, though, as I can write
let c1: Any<Collection> = …
let c2: Any<Collection> = c1

That’s why I think that we could just drop Any<Collection> and simply write Collection.

-Thorsten

···

Am 09.06.2016 um 18:49 schrieb Dave Abrahams via swift-evolution <swift-evolution@swift.org>:

on Wed Jun 08 2016, Jordan Rose <swift-evolution@swift.org> wrote:

On Jun 8, 2016, at 13:16, Dave Abrahams via swift-evolution >>> <swift-evolution@swift.org> wrote:

on Wed Jun 08 2016, Thorsten Seitz >> >>> <swift-evolution@swift.org >>> <mailto:swift-evolution@swift.org>> >>> wrote:

Ah, thanks, I forgot! I still consider this a bug, though (will have
to read up again what the reasons are for that behavior).

Yes, but in the case of the issue we're discussing, the choices are:

1. Omit from the existential's API any protocol requirements that depend
on Self or associated types, in which case it *can't* conform to
itself because it doesn't fulfill the requirements.

2. Erase type relationships and trap at runtime when they don't line up.

Matthew has been arguing against #2, but you can't “fix the bug” without
it.

#1 has been my preference for a while as well, at least as a starting
point.

I should point out that with the resyntaxing of existentials to
Any<Protocols...>, the idea that Collection's existential doesn't
conform to Collection becomes far less absurd than it was, so maybe this
is not so bad.

>
>>
>>> Introducing a language (not library) mechanism that exposes members on
>>> generalized existentials in a way that relies on runtime traps for
>>> type safety feels to me like a pretty dramatic turn agains the stated
>>> priority of safety. It will mean you must understand exactly what is
>>> going on and be extremely careful to use generalized existentials
>>> without causing crashes. This will either make Swift code much more
>>> crashy or will scare people away from using generalized existentials
>>> (and maybe both).
>>
>> I don't accept either of those statements without seeing some analysis
>> of the use-cases. For example, I don't believe that AnyCollection et al
>> are particularly crash-prone. The likelihood that you'll use the wrong
>> index type with a collection is very, very low. I'm less certain of
>> what happens with Self requirements in real cases.
>
> But again, I believe this is an exceptional case as the precondition
> is explicitly stated in the semantics of the protocol.

IIUC, it has been cited by Doug as the exemplar of the
predominantly-requested case by a 10:1 ratio!

At the risk of sounding waffly, I see both sides of the argument.

Java is typesafe in the formal sense (can't perform an operation on a type that doesn't actually support it); almost certainly moreso than Swift (which lets you do wonderful things like reinterpret casting, C-style direct memory manipulation...).

However, in many cases Swift provides better compile-time assurances than Java. In Java, any reference might actually be a null reference waiting to cause a NullPointerException unless you explicitly check it in code (and what if you forget to check it somewhere?). In Swift, (aside from IUOs and a few other features) if your program compiles successfully the type checker has proven that your software will never try calling a method upon nil. And if you do use IUOs the postfix exclamation marks clearly mark spots in your code that might cause trouble.

In the same way I don't think trapping at runtime if an existential satisfying a generic type parameter causes a type error is the best solution to this problem. What we are doing is allowing a type that makes a weak guarantee to stand in for a type that makes a strong guarantee, and catching mismatches between those guarantees if/when they pop up. There's no easy way for someone perusing the code to tell that such a mismatch might occur, equivalent to IUO's postfix !.

What I prefer is to explicitly "promote" the type with the weak guarantee to a type with the stronger guarantee using the existential opening mechanism that we've been throwing around. That way, it's very clear what is going on: if the existentials in question can't serve as generic parameters to the function you want to pass them to, you can fatalError() or do something else. Or you can try force-opening with `as!` and mark a potential trouble spot in the same way an IUO does. Once it succeeds you know you have the strong guarantees that will cause your generic member to alway work the way it should.

Yes, there should never be crashes for calling a wrong method. That is what *type* safety means.
You still can have crashes when calling a method with wrong arguments. To make that type safe you would need a type system with dependent typing.

I think that Swift should not only always be safe but that it should always be type safe, too.

-Thorsten

···

Am 09.06.2016 um 19:24 schrieb Austin Zheng via swift-evolution <swift-evolution@swift.org>:
On Thu, Jun 9, 2016 at 9:42 AM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
on Thu Jun 09 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:

With the proposed mechanism, I expect that Any<Collection where .Element == T> should be less prone to runtime trapping than AnyCollection<T>, because we can reify the concept that each instance of the existential has a concrete Index type associated with it, rather than relying upon a type-erased AnyIndex shared among all AnyCollections. (That being said, I don't know if AnyCollection is particularly problematic in practical use. I also don't know if other type-erased wrappers users might define might be more or less prone to this type of issue.)

> IMO the burden of proof should be on the side that proposes a
> mechanism to introduce traps, not the side that proposes avoiding
> them.

If you really want to make this about sides and burdens, the burden of
proof always rests with the side proposing to extend the language. We
shouldn't be making changes without understanding how they will play out
in real use-cases.

Yes, I really want to see real-world use cases.

>>
>> Neither of these look like they actually make *use* of the fact that
>> there's type erasure involved (and therefore should probably be written
>> as generics?). The interesting cases with Any<Collection...>, for the
>> purposes of this discussion, arise when you have multiple instances of
>> the same existential type that wrap different concrete types.
>
> One use case I have found is to work around the lack of higher-kinder
> types.

Really, now: a use-case for feature A that is a workaround for the lack
of feature B hardly justifies adding feature A! We do want to add
higher-kinded types eventually.

I just want to say, this made my day.

Most of the examples in the document Matthew linked were meant to be didactic, not practical. I would love to swap them out for more useful ones.

>>
>> Another problem I see: in this new world, what is the model for choosing
>> whether to write a function as a protocol extension/generic, or as a
>> regular function taking existential parameters? Given that either of
>> the above could have been written either way, we need to be able to
>> answer that question. When existentials don't conform to their
>> protocols, it seems to me that the most general thing to do is use
>> existentials whenever you can, and only resort to using generics when
>> forced by the type system. This does not seem like a particularly good
>> programming model to me, but I might be convinced otherwise.
>
> That doesn’t seem like a particularly good programming model to me either.
>
> The rule of thumb I am operating with for protocols with Self or
> associated type requirements is to prefer generics and use type
> erasure / existentials when that isn’t possible. For example, when
> heterogeneity is required or when you can’t form the necessary type in
> a protocol requirement (as in the preceding example).
>
> This heuristic has been working out pretty well for me thus far.

I do worry a bit that people will choose the opposite heuristic.

If we are to introduce this feature, I expect that the community would go through another "reference vs value types - which is right for you?" type discussion. It's true you can model a lot of things with classes where structs would be more appropriate, but this doesn't seem to be a big issue.

I would be very interested in affordances that can steer people towards using generics when the dynamic nature of existentials isn't necessary.

______________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Ah, thanks, I forgot! I still consider this a bug, though (will have
to read up again what the reasons are for that behavior).

Yes, but in the case of the issue we're discussing, the choices are:

1. Omit from the existential's API any protocol requirements that depend
on Self or associated types, in which case it *can't* conform to
itself because it doesn't fulfill the requirements.

2. Erase type relationships and trap at runtime when they don't line up.

Matthew has been arguing against #2, but you can't “fix the bug” without
it.

#1 has been my preference for a while as well, at least as a starting
point.

I should point out that with the resyntaxing of existentials to
Any<Protocols...>, the idea that Collection's existential doesn't
conform to Collection becomes far less absurd than it was, so maybe this
is not so bad.

the issue I have will all proposed Any<…> so far is that they are rather heavy, and in the case of a replacement for the obj-c id<UITableViewDataSource> or worse MyTVC<UITableViewDataSource> (I know the example is absurd for it stands for a specific concrete type conforming), it involves a bit of magic. So played with a little toy syntax starting from the sugaring you are doing for literals:

Array<X> —>
Dict<X,Y> —> [X:Y]

stretching it to consider that existential are a degenerate form of literal notation, then the following would not look entirely out of place

[last example unfinished - missing opening syntax]

···

On Jun 9, 2016, at 6:49 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Wed Jun 08 2016, Jordan Rose <swift-evolution@swift.org> wrote:

On Jun 8, 2016, at 13:16, Dave Abrahams via swift-evolution >>> <swift-evolution@swift.org> wrote:
on Wed Jun 08 2016, Thorsten Seitz >> >>> <swift-evolution@swift.org >>> <mailto:swift-evolution@swift.org>> >>> wrote:

--
Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Exactly. But much simpler cases will also either have to trap at
runtime or be prohibited outright:

func subscript_<C: Collection>(c: C, i: C.Index) -> C.Collection.Element {
   return c[i]
}

typealias IntCollection = Any<Collection where Element == Int>
let c1: IntCollection = ...
let c2: IntCollection = c1[3..<10]
let c3: IntCollection = ...
let c4: IntCollection = c1.reversed()

// Note: the underlying index types are the same, and are supposed to
// interoperate. What do you do (work/trap/nocompile)?
_ = subscript_(c1, c2.startIndex)

// The underlying index types happen to be the same, and are not
// supposed to interoperate. What do you do (silently “work”/trap/nocompile)?
_ = subscript_(c1, c3.startIndex)

// The underlying index types are different. What do you do (trap/nocompile)?
_ = subscript_(c1, c4.startIndex)

All of these are type errors. All we know about c1, c2, c3 and c4 is that they are of type `Any<Collection where Element == Int>` which does not constrain the Index type, so all we know is that each variable can have a different Index type. So, the type system can only say, no, these are type errors.

On a second thought this example reads a little bit like the following:

let x1: Object = …
let x2: Object = „hello"

// work/trap/nocompile?
x1.characters

// x2 happens to be a String
// work/trap/nocompile?
x2.characters

I think we all agree that all these are type errors even though we know that x2 contains a String and it might be useful to work in some cases. Maybe :-)

The same applies to the examples above IMO. The static knowledge is not sufficient for those examples to compile.

But thinking of path dependent types the following should work:

scala will soon have a ‘official' LLVM back-end… so that might soon be accessible to iOS devs

···

On Jun 9, 2016, at 7:29 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 09.06.2016 um 00:18 schrieb Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

let c1: IntCollection = …
let c2: c1.Type = c1[3..<10]

subscript_(c1, c2.startIndex) // ok!

-Thorsten

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Regards
(From mobile)

Regards
(From mobile)

Ah, thanks, I forgot! I still consider this a bug, though (will have
to read up again what the reasons are for that behavior).

Yes, but in the case of the issue we're discussing, the choices are:

1. Omit from the existential's API any protocol requirements that depend
on Self or associated types, in which case it *can't* conform to
itself because it doesn't fulfill the requirements.

2. Erase type relationships and trap at runtime when they don't line up.

Matthew has been arguing against #2, but you can't “fix the bug” without
it.

#1 has been my preference for a while as well, at least as a starting
point.

I should point out that with the resyntaxing of existentials to
Any<Protocols...>, the idea that Collection's existential doesn't
conform to Collection becomes far less absurd than it was, so maybe this
is not so bad.

I think the problem is more that Any<Collection> does not conform to a specific value for a type parameter T: Collection

What I mean by this is that `Collection` denotes a type family, a generic parameter `T: Collection` denotes a specific (though unknown) member of that type family and `Any<Collection>` denotes the type family again, so there is really no point in writing Any<Collection> IMO.
The type family cannot conform to T because T is just one fixed member of it.
It conforms to itself, though, as I can write
let c1: Any<Collection> = …
let c2: Any<Collection> = c1

That’s why I think that we could just drop Any<Collection> and simply write Collection.

Let me expand that a bit:

Actually all this talk about existentials vs. generics or protocols vs. classes has had me confused somewhat and I think there are still some misconceptions present on this list sometimes, so I’ll try to clear them up:

(1) misconception: protocols with associated types are somehow very different from generics

I don’t think they are and I will explain why. The only difference is the way the type parameters are bound: generics use explicit parameter lists whereas protocols use inheritance. That has some advantages (think long parameter lists of generics) and some disadvantages.
These ways are dual in a notation sense: generic types have to have all parameters bound whereas protocols cannot bind any of them.
The „existential“ notation `Any<>` being discussed on this list is nothing more than adding the ability to protocols to bind the parameters to be used just like Java’s wildcards are adding the opposite feature to generics, namely not having to bind all parameters.

btw, i tried to to see if Any<> could have a simpler alternative
Extended existential · GitHub

As you know I like using `&` as type intersection operator. But you write "The syntax leave a void when it comes to expressing the so called Top type:“
Why? Just give the top type a name, e.g. `Any` and you are done.

Yes.. I just don't like magic. I think that all types should be expressable with a syntax and that you can then decide to alias one parsing case with a specific name. Not the other way around.

Well, I think that would be backwards, because we have a nominal type system. That means that the syntax for a type is just its name.

I realize we do not understand each other.
The words we use now to describe a behavior are just that, words. As is the concepts they describe are useless to the computer running the compiler.
So we need to map these concepts into a heuristic that a fast but dumb computer will be able to reason with. The code to do that will be either clean and organized, or it will look contrived and full of different paths that will be difficult to mesh together. Of all the possible ways in which swift can behave as a language, some will lead to the former, others to the latter code. I think this is not fate or something you find out after the decision was made and you struggle to carry it through. This is something that can partially be predicted. One of the tools for such prediction is to translate the concepts into a grammar that will show formalize the logic. The simpler the logic, the cleaner (not simple) the final code.

Saying that the syntax for a type is a name is of no use whatsoever for the compiler implementer. It is so universally true that it cannot help in any way whatsoever decide the shape of the swift grammar, much less the structure of the c++ code implementing it.

`&` is a type operator which creates a new type from its operands.
`where` clauses add constraints to types.
But it all starts with a type's name.

The only magic would be that all type definitions (`protocol` etc.) which do not give a supertype they conform to, will implicitly conform to `Any`, i.e.

I call magic the core notions of swift that cannot be expressed in swift but have to parachutted in by the compiler. If you read Chris' initial message you will see that he said as much, that adopting P&Q as syntax was leaving a gap that the compiler would have to magically fill.

protocol Foo { … }

means

protocol Foo : Any { … }

That’s less magic than creating a special syntax just to express the top type.

I will try again... Would it be easier to understand it if instead of magic I said arbitrary? You are creating a special case: you decide arbitrarily that the special series of characters 'A' 'n' 'y' with this precise casing and without any spaces is going to be adorned with a special meaning when used in specific situations. This is not something you deduced. My approach is exactly the opposite: _ does not describe the top type because I have a special affinity with these characters, it describes the top type because it is the only possible logical conclusion from having followed a set of rules attached to a syntax that describes ALL existentials. And because it is not particularly savory, I typealias it to something else. But this is not in the compiler, it us in the standard linrary... inside swift, not outside.

Ok, that's a good argument. But your proposal does not contain those rules.
Why should _ describe the top type? You just say this in your proposal, but you do not define rules what _ means.
_[A,B]

don't know where this comes from. This is incorrect and the parser would reject it.

describes the type intersection of A and B, i.e. it contains all members of A which are also members of B. _ does not list any types, so one might conclude that it has no members and therefore must be the bottom type!

Are you saying that protocol<> is the bottom type of Swift then? Because if you disect the logic it follows to explain why it is a top type and apply the exact same reasoning to _ then you should reach the same conclusion {same effects produce the same conclusions}

Or _[A,B] in reality means _[A,B,Any],i.e. A & B & Any which of course is equal to A & B. Then _ would just mean Any, but we would have had to introduce the top type explicitly again.

From cause to effects. Any is an effect (a conclusion we reach) rather than a cause (something we posit before starting the discussion). Like I explained, Any is a stdlib provided convenient typealias for _ as opposed to a parachuted concept born inside the compiler. Btw, it means a different stdlib could redefine it as All, without altering the compiler. Any is not core to swift's structure, but _ is because it is borne out of the mechanical uniformity of the grammar.

On the other hand if we declare a protocol Any and define protocol A {...} to mean protocol A: Any {...} there is not really something special in the compiler except for adding the conformance which seems very minor to me. Everything else is just standard behavior of the type system.

You are back to adding some magic that I just demonstrated is not required if the grammar is simple (not that i could add it to my grammar to please you, but then it would not eliminate any of the other definitions, and would just complicate the parser and type checker to ensure that people use it correctly. Per Ockham's principle, the system i describe is a better alternative.

Read Chris' original announcement.. He describes P&Q as it would be adopted today as being just a CORNER CASE of a single general principal... a single grammar that can work today to describe P&Q as well as generalize existentials tomorrow. No special treatment, single parser, single rule. I don't like to write un-necessary IF statements in code.

Sorry, I do not understand what you mean here: where would you have to write unnecessary if-statements?

Close your eyes and make a mental representation of what the code to implement the Any<....> proposal will necessarily look like... that is what I am referring to. That code cannot not be uggly. Because some of of things a parser typically should catch are not discernsble, and therefore have to be differed to the type checker. By comparison, look at the grammar I wrote and picture the code to implement it. The picture will look very different, and the parser can already eliminate a lot of things that never reach the type checker.

Why should the top type have special *syntax*? It is just the type all other types conform to. No need to do something special here and therefore no need to invent an alternative syntax like `Any<>` or the alternative from your gist which is rather confusing IMO (and I don’t like the special case given to classes in the syntax).

The _ case is just a degenerate case of the syntax, showing that it is expressible inside, as opposoed to have to define a contextual keyword (read the SourceKit code). And then it is aliasable. Part of the problems in swift today to me is that some things are no doable in swift, so the compiler must contain that semantic. This here is just one example, but there are others. These are notions that the standard library has to defer to the compiler. Some of them have been tabled for 4.0 it seems. My point here was that it is not fate, and choosing the syntax carefully for existentials (whichever it is, i don't really care inthe end) would prevent having to add a magic keyword for to top type to the compiler and to SourceKit again (they are trying to remove them).

`Any` would *not* be a keyword. It is just a type name like `Collection` or `Int`. Like I said, the only magic would be in adding `: Any` to type definitions without a conforming clause.

I hope that by now you understand what magic I was talking about.

As for the current Any<...> proposal for generalizing existentials it is IMHO cluncky and magic. There is nothing stopping us mechanically from entering Any<UITableView, UIButton>. To me that is the holemark of bad design. In what I tested you just can't do it and the reason is clear. Of course when I

`Any<UITableView, UIButton>` is just an intersection type and would be written UITableView & UIButton. And, yes, that would be ok, because it would be just the empty set, i.e. the bottom type (which has no members). There is no magic involved, it is just the normal result of an intersection: each type is a set containing all instances of this type (instances conforming to it)

You can't be serious? You are saying that to you the ide should not be telling us we are writting an absurdity? And this one was obvious, but it gets a lot worse with more complex types.

Take a screenshot next time you see a "let x:Any<UITableView, UIButton>" in an app and send it to me. The absurdity would be for the compiler to generate a binary, the app to start, and us to wonder why x the code depending on x would never execute. Seems obvious to you why now?

I’m taking an example from Gavin King’s presentation about Ceylon instead which can be found here: https://greenjug.java.net/resources/CeylonEastCoastTour20141016.pdf

my understanding is that dave is the type system designer. I will let the two of you finish this conversation and just read.

:-)

We’ll probably see after WWDC what he thinks…

Best regars

-Thorsten

···

Am 12.06.2016 um 17:40 schrieb L Mihalkovic <laurent.mihalkovic@gmail.com>:

On Jun 12, 2016, at 4:05 PM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

Am 12.06.2016 um 09:21 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com <mailto:laurent.mihalkovic@gmail.com>>:
On Jun 11, 2016, at 11:43 PM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

Am 11.06.2016 um 15:08 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com <mailto:laurent.mihalkovic@gmail.com>>:

On Jun 11, 2016, at 2:05 PM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

Am 11.06.2016 um 12:38 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com <mailto:laurent.mihalkovic@gmail.com>>:

On Jun 11, 2016, at 11:30 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

Am 11.06.2016 um 08:00 schrieb L. Mihalkovic <laurent.mihalkovic@gmail.com <mailto:laurent.mihalkovic@gmail.com>>:

On Jun 10, 2016, at 9:35 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Am 09.06.2016 um 19:50 schrieb Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Am 09.06.2016 um 18:49 schrieb Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
on Wed Jun 08 2016, Jordan Rose <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 8, 2016, at 13:16, Dave Abrahams via swift-evolution >>>>>>>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
on Wed Jun 08 2016, Thorsten Seitz >>>>>>>>>>>>> >>>>>>>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>>>>>>>>>> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> >>>>>>>>>>>>>> wrote:

very best

<PastedGraphic-1.tiff>

Object is the abstract supertype of all types representing definite values, i.e. which are not null. Remember that in Ceylon Optional<T> is modeled as type union T | Null, where Null is the type containing the single member null (i.e. nil in Swift).
Element is the type parameter for the elements of Iterable.

Element might be String | Null, for example (in Ceylon abbreviated as String? just like in Swift).
Element & Object can only be String, because Null & Object is empty, i.e. is the bottom type:

(String | Null) & Object = (String & Object) | (Null & Object) = String | Bottom = String

So, while you don’t see UITableView & UIButton here, there is Null & Object at work which yields the bottom type, just like UITableView & UIButton would.
If this was forbidden, then how would the type system be able to simplify the expression (String | Null) & Object?

not debatting past here I can't see how it relates (it is interesting but you may want to look at the whole thing from a 'whats usefull' perspective, rather than as 'whats combinatorially possible‘

Like the slide above hopefully demonstrated this is not just a nice theoretical exercise but it *is* useful if you use the whole abstraction and do not try to arbitrarily single out special cases as forbidden.

Of course, writing UITableView & UIButton is not useful, but writing something like T & Object is, but it needs the whole general mechanism to work.
(And that general mechanism will show you automatically that UITableView & UIButton is not useful, because you can’t call any method on it, *without* having to introduce compiler magic or IDE magic to explicitly forbid this.)

Hope that made sense now :-)

-Thorsten

Why is an empty intersection absurd? The beauty is that all type expressions sort out automatically by applying simple set rules. And it actually does make sense!

struct Set<T> {
   // answer any one element
   func any() -> T?
}
func intersect<T, U>(a: Set<T>, b: Set<U>) -> Set<T & U> {...}

let x: Set<UITableView> = ...
let y: Set<UIButton> = ..
let z = intersect(x, y) // has type Set<Bottom>
z.any // has type Optional<Bottom> and therefore can only be nil

Therefore the type system statically knows that the intersection of those sets is always empty. No need to declare this as invalid. It just works correctly and gives the results you would expect without need for special casing and compiler magic (which we both would like to keep small).

-Thorsten

. Intersecting two sets might result in an empty set. The type denoting the empty set is the bottom type which is the subtype of all types and might be called `Nothing` or `Never` or `Bottom`.
Ceylon makes very nice use of type intersections and type unions and the beautiful thing is that these type operations really just work like you would expect if you think of types as sets (which is the standard definition for a type AFAIK). No surprises and no magic there!

So it is *not* a sign of bad design, quite to the contrary! What did you test it with? Probably not Ceylon, because otherwise you would have seen that it just works.

Hey, who knows, ceylon may one day come to llvm...

say you can't I do not mean that your keyboard will zap u if you try. But the way to verify correctness is different... and in a world where xcode would be a good ide, then code completion would even show you why and actually truly assist (because the grammar i tested makes really makes it possible). I know that xcode is only as good as SourceKit lets it be.

Essentially `Any<Collection>` in Swift is just the same as `Collection<?>` in Java (assuming for comparability’s sake that Swift’s Collection had no additional associated types; otherwise I would just have to introduce a Collection<Element, Index> in Java).

Likewise `Any<Collection where .Element: Number>` is just the same as `Collection<? extends Number>` in Java.

Java supports co/contra variant params

Java has no declaration-site variance like Ceylon or Scala have (see e.g. Eclipse Ceylon™ | projects.eclipse.org).

Java’s wildcards are a way to express use-site variance. The proposed `Any<>` does just the same.

And just like Collection<?> does not conform to a type parameter `T extends Collection<?>` because Collection<?> is the type `forall E. Collection<E>` whereas `T extends Collection<?>` is the type `T. Collection<T>` for a given T.

This picture is accurate today, but there are going to be more serious differences after 10 no date is currently geven for when it will come)

You mean Java 10?

Yes. After 10. Than is their only date hint.

In essence protocols with associated types are like generics with wildcards.

Yes, java has kept everything within its generics system rather than split parts out. Something people may not immediately think about with respect to the 2 generic systems is that when u call a func<T>capture(T t){} in java with a wildcard you are doing a compile time capture only (to avoid the dreaded unsafe casts), whereas it is really nice to do the same in swift and subsequently be able to access T.Type and see that it is not Any. The closest u ever get to that type at runtime in java is via generics introspection, but u still can't do everything ( like no new T() ). But today the bridging between existential types and generics is definitely a work in progress.

Coming back to the questions whether (a) allowing existentials to be used as types is useful and (b) whether sacrificing type safety would somehow be necessary for that, I think we can safely answer
(a) yes, it *is* useful to be able to use existentials like Any<Collection> as types, because wildcards are quite often needed and very useful in Java (they haven’t been added without a reason)

IMO they made java 8 (referring to streams). And even though the syntax for co/contra variance is pretty heavy, it is the foundation for all modern java code. The any-fication of the generics is going to open new doors as some of it will translate into a partial reification in the jvm. It seems the decision for now is to not use the extra info in java to retain binary compatibility with all the erased code out there, this is something scala might use in areas where it won't mind loosing java compatibility.

(b) no, sacrificing type safety does not make sense, as the experience with Java’s wildcards shows that this is not needed. Especially if something like path dependent types is used like proposed and some notation to open an existential’s type is added, which is both something that Java does not have.

I hope typesafe opening inside the " if let " syntax gets added. I know that chris is against sugaring, but I played if an implementation of

... x is String?
If let! x {}
That runs as
if let x = x {}

something equally short could be done here.

(2) misconception: POP is different from OOP

It is not. Protocols are just interfaces using subtyping like OOP has always done. They just use associated types instead of explicit type parameters for generics (see above). The more important distinction of Swift is emphasizing value types and making mutation safely available by enforcing copy semantics for value types.

Values are coming to the jvm, which will narrow this gap (like their view identity for value vs ref and the whole deep ==) . I also really like the cow approach of the swift runtime.

But protocols are not really different from interfaces in Java.

There is one big difference: default methods, but it seems swift will add that soon.

Swift already has default extension methods, doesn’t it?

Yes, and no. It has differently dispatched code that can be found to fill in the gap of the conformance req, yes. But Joe Grof (?) said that there are no reasons why these could not be added. Having true defaults could be one way to deal with optional comformance…

The dispatch issue only arises for extensions introducing a method that is not declared in a protocol. That is something you *cannot* do in Java. Java’s default methods are implementations for methods declared in interfaces. Swift’s extension methods providing defaults for methods declared in a protocol are dynamically dispatched and should work like Java’s default methods.

I think we should agree to disagree.

(One caveat exists with subclasses where the superclasses didn’t implement the method because I then am not allowed to `override` the default method but that is a bug IMO).

-Thorsten

protocol MyProto {
func mustImplement()
default canImplement() { }
}

Related data there:
[swift-evolution] [summary] Protocol extension method dispatch - was [Static Dispatch Pitfalls]

I also really like how extensions and conformance mix together in swift to bake retro-modelling in and the adapter pattern (spent enough years deep diving inside eclipse to appreciate it).

I would have preferred a unified model using just classes with real multiple inheritance like Eiffel has and value types just being a part of that similar to Eiffel’s `expanded` classes. But that ship has probably sailed a long time ago :-/

I like extensions very much (having used Smalltalk for a long time). I like enums for the pattern matching and structs as value types. But having to split protocols off instead of using abstract classes makes things more complicated IMO.

-1 i am old school c/c++... i really like protocol, struct, class, extensions, enums. It is a really nice mix that gives objc people room to grow, but I do miss how they are an integral part of generics (i protocols as a replacement and look forward to when they interact better) and namespaces+scoped-imports (c#)... Looking forward to where things go next

Yeah, namespacing/submodules/conflict resolution (when doing imports but also when conforming to multiple protocols which has just the same problems) are still missing. But I’m optimistic :-) Let’s complete generics first, then tackle existentials/type intersections.

I think I care more about existentials, but only because of the kind of java i wrote for a living. I just looked at a bunch of opensource swift libs (particularly for server side swift)... some of it is a real engineering disaster: 20+ folders, each with 2 source files... or 3 folders "extensions" "utils" "classes". Swift is currently not equiped for people to write big things with... I wish the team would address it sooner than later (look at their own c++ code to see the difference). IMHO import conflicts are more often the symptom of bad code than a real issue.

Alas, most languages suffer from poor module systems. A good module system should not only allow resolving conflicts between modules and making it possible to structure code well, but solve the issue around versioned modules so that is is possibly to safely use different versions of the same module in the same application.

-Thorsten

Cheers

-THorsten

So be it. But at least there should be no reasons for POP vs OOP wars ;-)
(I’d like to add that I liked Dave’s talks at last WWDC very much, it’s just that I don’t think that POP is something new or different.)

I used to thin that way. But today I think that although in broad brush strokes the similarities and bigger than the differences, there is room for making a bigger difference in the how.

-Thorsten

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

That would be a misunderstanding. I am a contributor, but other people
take the lead on the type system.

···

on Sun Jun 12 2016, L Mihalkovic <laurent.mihalkovic-AT-gmail.com> wrote:

My understanding is that dave is the type system designer.

--
Dave

, but haven't realized
that if you step around the type relationships encoded in Self
requirements and associated types you end up with types that appear to
interoperate but in fact trap at runtime unless used in exactly the
right way.

Trap at runtime? How so? Generalized existentials should still be
type-safe.

There are two choices when you erase static type relationships:

1. Acheive type-safety by trapping at runtime

FloatingPoint(3.0 as Float) + FloatingPoint(3.0 as Double) // trap

2. Don't expose protocol requirements that involve these relationships,
which would prevent the code above from compiling and prevent
FloatingPoint from conforming to itself.

Or are you talking about the hypothetical types / behaviors people
think they want when they don’t fully understand what is happening...

I don't know what you mean here. I think generalized existentials will
be nice to have, but I think most people will want them to do something
they can't possibly do.

Exactly. What I meant is that people think they want that expression
to compile because they don’t understand that the only thing it can do
is trap. I said “hypothetical” because producing a compile time error
rather than a runtime trap is the only sane thing to do. Your comment
surprised me because I can’t imagine we would move forward in Swift
with the approach of trapping.

I would very much like to be able to create instances of “Collection
where Element == Int” so we can throw away the wrappers in the stdlib.
That will require some type mismatches to be caught at runtime via
trapping.

For invalid index because the existential accepts a type erased index?

Exactly.

How do you decide where to draw the line here? It feels like a very
slippery slope for a language where safety is a stated priority to
start adopting a strategy of runtime trapping for something as
fundamental as how you expose members on an existential.

If you don't do this, the alternative is that “Collection where Element
== Int” does not conform to Collection.

This isn’t directly related to having self or associated type
requirements. It is true of all existentials.

That is just an implementation limitation today, IIUC. What I'm talking
about here would make it impossible for some to do that.

If it is just an implementation limitation I am happy to hear that.

If that changes for simple existentials and generalized existentials
expose all members (as in the latest draft of the proposal) maybe it
will be possible for all existentials to conform to their protocol.

Not without introducing runtime traps. See my “subscript function”
example.

That's weird and not very
useful. You could expose all the methods that were on protocol
extensions of Collection on this existential, unless they used
associated types other than the element type. But you couldn't pass the
existential to a generic function like

func scrambled<C: Collection>(_ c: C) -> [C.Element]

IMO you should *have* to introduce unsafe behavior like that manually.

Collection where Element == Int & Index == *

?

I didn’t mean directly through the type of the existential.

My question is, why not? That is still explicit.

It’s not explicit in the sense that nobody wrote `fatalError` or
similar in their code. It’s too easy to write something like that
without realizing that it introduces the possibility of a crash. If
we adopt syntax like that to introduce an existential that introduces
traps we should at least require members that can be trap to be
invoked using a `!` suffix or something like that to make it clear to
users that a trap will happen if they are not extremely careful when
using that member.

More generally though, I don’t want the rules of the language to be
written in a way that causes the compiler to synthesize traps in such
a general way.

The existential should not introduce a precondition that isn’t already
present in the semantics of the protocol itself. If the semantics of
the protocol do not place preconditions on arguments beyond their type
(such as “must be a valid index into this specific instance”) the
compiler should not allow the existential to conform if a trap is
required in some circumstances. That is a new precondition and
therefore the existential does not actually fulfill the requirements
of the protocol.

I could *maybe* live with a solution where protocol requirements are
marked as trapping, etc depending on the specific argument received at
runtime. This is a total straw man syntax, but maybe `IndexableBase`
would declare the subscript `@trapping` (probably something different
but I hope this communicates the idea). This alerts users to the fact
that they need to be extra careful - not any value of `Self.Index` is
valid and you can get a crash if you’re not careful.

Having this semantic explicit in the definition of the protocol opens
the door to maybe considering an existential synthesized by the
compiler that traps because it doesn’t introduce a new precondition
that wasn’t already present in the protocol.

I would want to give consideration to specific details of a proposal
along these lines before deciding how I feel about it, but I have a
more open mind to this approach than introducing traps not present in
the preconditions of the protocol.

/// You can subscript a collection with any valid index other than the
/// collection's end index. The end index refers to the position one past
/// the last element of a collection, so it doesn't correspond with an
/// element.
///
/// - Parameter position: The position of the element to access. `position`
/// must be a valid index of the collection that is not equal to the
/// `endIndex` property.
@trapping public subscript(position: Self.Index) -> Self._Element { get }

One obvious mechanism for introducing unsafe behavior is to write
manual type erasure wrappers like we do today.

Another possibility would be to allow extending the existential type
(not the protocol). This would allow you to write overloads on the
Collection existential that takes some kind of type erased index if
that is what you want and either trap if you receive an invalid index
or better (IMO) return an `Element?`. I’m not sure how extensions on
existentials might be implemented, but this is an example of the kind
of operation you might want available on it that you wouldn’t want
available on all Collection types.

Collection indices are already something that isn’t fully statically
safe so I understand why you might want to allow this.

By the same measure, so are Ints :-)

The fact that a type's methods have preconditions does *not* make it
“statically unsafe.”

That depends on what you mean by safe. Sure, those methods aren’t
going corrupt memory, but they *are* going to explicitly and
intentionally crash for some inputs. That doesn’t qualify as “fully
safe” IMO.

Please pick a term other than “unsafe” here; it's not unsafe in the
sense we mean the word in Swift. It's safe in exactly the same way that
array indexes and integers are. When you violate a precondition, it
traps.

I am happy to use any word you like here.

Can you clarify what you mean by the word safe in Swift? It doesn’t
appear to be limited to memory safety in the public about page
Swift.org - About Swift <https://swift.org/about/&gt;:

I mean memory- and type-safe.

Safe. The most obvious way to write code should also behave in a safe
manner. Undefined behavior is the enemy of safety, and developer
mistakes should be caught before software is in production. Opting for
safety sometimes means Swift will feel strict, but we believe that
clarity saves time in the long run.

Safety

Swift was designed from the outset to be safer than C-based languages,
and eliminates entire classes of unsafe code. Variables are always
initialized before use, arrays and integers are checked for overflow,
and memory is managed automatically. Syntax is tuned to make it easy
to define your intent — for example, simple three-character keywords
define a variable (var) or constant (let).

Another safety feature is that by default Swift objects can never be
nil, and trying to make or use a nil object will results in a
compile-time error. This makes writing code much cleaner and safer,
and prevents a common cause of runtime crashes. However, there are
cases where nil is appropriate, and for these situations Swift has an
innovative feature known as optionals. An optional may contain nil,
but Swift syntax forces you to safely deal with it using ? to indicate
to the compiler you understand the behavior and will handle it safely.

This positioning statement makes it appear as if preventing common
causes of crashes falls within the meaning of safe that Swift is
using. Having existentials introduce new preconditions and traps when
they are not met does not seem aligned with that goal IMO.

Static typing “increases safety,” in the casual sense. That doesn't
mean that an operation that traps on a failed precondition check is
“unsafe.”

The user doesn't do anything “manual” to introduce that trapping
behavior for integers. Preconditions are a natural part of most types.

The user doesn’t, but isn’t the overflow trap implemented in the
standard library?

Whether it is or is not is an implementation detail.

Regardless, this is a specific case that has been given explicit
design attention by humans. The precondition is designed, not
introduced by compiler rules that haven’t considered the specific case
in question.

But I don’t think having the language's existentials do this
automatically is the right approach. Maybe there is another
approach that could be used in targeted use cases where the less
safe behavior makes sense and is carefully designed.

Whether it makes sense or not really depends on the use-cases. There's
little point in generalizing existentials if the result isn't very useful.

Usefulness depends on your perspective.

Of course. As I've said, let's look at the use cases.

Agree. We can consider those in depth when the time comes to ramp up
discussion of Austin’s proposal.

I have run into several scenarios where they would be very useful
without needing to be prone to crashes when used incorrectly. One
obvious basic use case is storing things in a heterogenous collection
where you bind .

bind what?

Sorry, I must have gotten distracted and not finished that paragraph.
I meant to say bind the associated types that are necessary for your
use case. Sometimes you bind *all* of the associated types to
concrete types and the protocol has no `Self` requirements. In that
case there is no trouble at all in conforming the type-erased
“existential" to the protocol itself. Austin’s proposal would
eliminate the need to manually write these “existentials” manually.

The way to find out is to take a look at the examples we currently have
of protocols with associated types or Self requirements and consider
what you'd be able to do with their existentials if type relationships
couldn't be erased.

We have known use-cases, currently emulated in the standard library, for
existentials with erased type relationships. *If* these represent the
predominant use cases for something like generalized existentials, it
seems to me that the language feature should support that. Note: I have
not seen anyone build an emulation of the other kind of generalized
existential. My theory: there's a good reason for that :-).

AFAIK (and I could be wrong) the only rules in the language that
require the compiler to synthesize a trap except using a nil IUO, `!`
on a nil Optional, and an invalid `as` cast . These are all
syntactically explicit unsafe / dangerous operations. All other traps
are in the standard library (array index, overflow, etc). Most
important about all of these cases is that they have received direct
human consideration.

There is no distinction in the user model between what might be
synthesized by the language and what appears on standard library types.

Maybe I shouldn’t have made that distinction.

The point I am trying to emphasize is that each of these are special
cases that have received direct human consideration. The potential
for a trap is not introduced by language rules that apply to
user-defined constructs in without consideration of the specific
details of that construct.

Introducing a language (not library) mechanism that exposes members on
generalized existentials in a way that relies on runtime traps for
type safety feels to me like a pretty dramatic turn agains the stated
priority of safety. It will mean you must understand exactly what is
going on and be extremely careful to use generalized existentials
without causing crashes. This will either make Swift code much more
crashy or will scare people away from using generalized existentials
(and maybe both).

I don't accept either of those statements without seeing some analysis
of the use-cases. For example, I don't believe that AnyCollection et al
are particularly crash-prone. The likelihood that you'll use the wrong
index type with a collection is very, very low. I'm less certain of
what happens with Self requirements in real cases.

But again, I believe this is an exceptional case as the precondition
is explicitly stated in the semantics of the protocol.

IIUC, it has been cited by Doug as the exemplar of the
predominantly-requested case by a 10:1 ratio!

In terms of forming the existential, storing it in variables,
accepting arguments of that type, etc yes. I don’t know how many of
those requests expect it to conform to the protocol and expect to be
able to use it in generic code constrained to the protocol.

IMO the burden of proof should be on the side that proposes a
mechanism to introduce traps, not the side that proposes avoiding
them.

If you really want to make this about sides and burdens, the burden of
proof always rests with the side proposing to extend the language. We
shouldn't be making changes without understanding how they will play out
in real use-cases.

I agree with this. But if we are discussing two different options for
extending the language I think the option that doesn’t introduce
crashes should be preferred without pretty compelling reasons to
choose the option that can introduce crashes.

Neither of those outcomes is good.

Collection indices are a somewhat special case as there is already a
strong precondition that people are familiar with because it would be
too costly to performance and arguably too annoying to deal with an
Optional result in every array lookup. IMO that is why the library is
able to get away with it in the current type erased AnyCollection.
But this is not a good model for exposing any members on an
existential that do not already have a strong precondition that causes
a trap when violated.

I think a big reason why you maybe haven’t seen a lot of examples of
people writing type erased “existentials" is because it is a huge pain
in the neck to write this stuff manually today. People may be
designing around the need for them. I haven’t seen a huge sampling of
type erased “existentials" other people are writing but I haven’t
written any that introduce a trap like this. The only traps are in
the “abstract" base class whose methods will never be called (and
wouldn’t even be implemented if they could be marked abstract).

What specific things do you think we need to be able to do that rely
on the compiler synthesizing a trap in the way it exposes the members
of the existential?

I don't know. I'm saying, I don't think we understand the use-cases
well enough to make a determination.

That’s fair. I agree that use cases should be carefully considered.

Here are a few examples from Austin’s proposal that safely use
existential collections. I don’t understand why you think this
approach is insufficient. Maybe you could supply a concrete example
of a use case that can’t be written with the mechanism in Austin’s
proposal.

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure <https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure&gt; <https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure&lt;https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#associated-types-and-member-exposure&gt;&gt;

let a : Any<Collection>

// A variable whose type is the Index associated type of the underlying
// concrete type of 'a'.
let theIndex : a.Index = ...

// A variable whose type is the Element associated type of the underlying
// concrete type of 'a'.
let theElement : a.Element = ...

// Given a mutable collection, swap its first and last items.
// Not a generic function.
func swapFirstAndLast(inout collection: Any<BidirectionalMutableCollection>) {
  // firstIndex and lastIndex both have type "collection.Index"
  guard let firstIndex = collection.startIndex,
      lastIndex = collection.endIndex?.predecessor(collection) where lastIndex != firstIndex else {
          print("Nothing to do")
          return
  }

  // oldFirstItem has type "collection.Element"
  let oldFirstItem = collection[firstIndex]

  collection[firstIndex] = collection[lastIndex]
  collection[lastIndex] = oldFirstItem
}

var a : Any<BidirectionalMutableCollection where .Element == String> = ...

let input = "West Meoley"

// Not actually necessary, since the compiler knows "a.Element" is String.
// A fully constrained anonymous associated type is synonymous with the concrete
// type it's forced to take on, and the two are interchangeable.
// However, 'as' casting is still available if desired.
let anonymousInput = input as a.Element

a[a.startIndex] = anonymousInput

// as mentioned, this also works:
a[a.startIndex] = input

// If the collection allows it, set the first element in the collection to a given string.
func setFirstElementIn(inout collection: Any<Collection> toString string: String) {
  if let element = string as? collection.Element {
      // At this point, 'element' is of type "collection.Element"
      collection[collection.startIndex] = element
  }
}

Neither of these look like they actually make *use* of the fact that
there's type erasure involved (and therefore should probably be written
as generics?). The interesting cases with Any<Collection...>, for the
purposes of this discussion, arise when you have multiple instances of
the same existential type that wrap different concrete types.

One use case I have found is to work around the lack of higher-kinder
types.

Really, now: a use-case for feature A that is a workaround for the lack
of feature B hardly justifies adding feature A! We do want to add
higher-kinded types eventually.

Good to know. I thought higher-kinder types were on the “maybe if
someone shows a compelling enough use case” list. AFAIK this is the
first time a member of the core team has stated the intent to add
them.

Well, please don't take this as a formal statement on behalf of the
team. IIUC, the team is generally interested in having this feature.

If that is the case I agree that this use case isn’t relevant. The
workaround isn’t great because it loses type information that is
critical to the optimizer (but it’s all we have available today).

If you have a protocol where specific implementations will return
different types, but all conform to a second protocol you can define
the protocol in terms of a generic type-erased wrapper which conforms
to the second protocol and accepts type arguments that match the
associated types (thus binding the associated types to concrete
types). I have found this to be a useful technique (granted it is a
workaround and I’m not sure how useful it would continue to be if
Swift eventually gets higher-kinder types).

Another problem I see: in this new world, what is the model for choosing
whether to write a function as a protocol extension/generic, or as a
regular function taking existential parameters? Given that either of
the above could have been written either way, we need to be able to
answer that question. When existentials don't conform to their
protocols, it seems to me that the most general thing to do is use
existentials whenever you can, and only resort to using generics when
forced by the type system. This does not seem like a particularly good
programming model to me, but I might be convinced otherwise.

That doesn’t seem like a particularly good programming model to me either.

The rule of thumb I am operating with for protocols with Self or
associated type requirements is to prefer generics and use type
erasure / existentials when that isn’t possible. For example, when
heterogeneity is required or when you can’t form the necessary type in
a protocol requirement (as in the preceding example).

This heuristic has been working out pretty well for me thus far.

I do worry a bit that people will choose the opposite heuristic.

It would be somewhat reassuring to me if we could prove to ourselves
that, using your heuristic, one is never forced to copy/paste a generic
function implementation into a corresponding function that uses
existentials.

The primary impact of introducing a language mechanism for generalized
existentials in my code would be to eliminate a lot of manual type
erasing boilerplate.

If your code has many manual type erasing wrappers corresponding to
protocols with associated types and/or Self requirements that also never
have to trap type mismatches, that would certainly be instructive
empirical data. Would you care to share the protocols and wrappers you
are talking about?

I put together a sample implementation of a Cocoa-like responder chain
in Swift a while ago when the “Swift dynamism” debate was raging.

It isn't intended to be a Swifty design. It is intended to be similar
to Cocoa and show techniques that can be used to do things similar to
Cocoa’s responder chain and targer-action in Swift. It uses a type
erased wrapper for actions that binds `Sender` while hiding the
concrete `Action` type and also the `Handler` associated type. It
cannot and should not conform to the protocol it is derived from and
could be replaced with the generalized existentials in Austin’s
proposal.

A minimalist responder chain implemented in pure Swift · GitHub
<https://gist.github.com/anandabits/ec26f67f682093cf18b170c21bcf433e&gt;

This is a good example to start with because it is related to a topic
that has been hotly debated and is clearly something a lot of people
want to be able to do.

It's great to start with, but if this is just one example, we still
technically have more examples in the standard library of the other kind
of wrapper. You said it would eliminate a lot of manual boilerplate. I
presume that means you have lots more examples like this one?

···

on Thu Jun 09 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:

On Jun 9, 2016, at 11:42 AM, Dave Abrahams <dabrahams@apple.com> wrote:
on Thu Jun 09 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 9, 2016, at 9:55 AM, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@apple.com>> wrote:
on Wed Jun 08 2016, Matthew Johnson <matthew-AT-anandabits.com >>>> <http://matthew-at-anandabits.com/&gt; >>>> <http://matthew-at-anandabits.com/ >>>> <http://matthew-at-anandabits.com/&gt;&gt;&gt; wrote:

On Jun 8, 2016, at 1:33 PM, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@apple.com>> wrote:
on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 7, 2016, at 9:15 PM, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@apple.com>> wrote:
on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com >>>>>>>> <http://matthew-at-anandabits.com/&gt; >>>>>>>> <http://matthew-at-anandabits.com/ >>>>>>>> <http://matthew-at-anandabits.com/&gt;&gt;&gt; wrote:

On Jun 7, 2016, at 4:13 PM, Dave Abrahams via >>>>>>>>>> swift-evolution <swift-evolution@swift.org >>>>>>>>>> <mailto:swift-evolution@swift.org>> wrote:
on Tue Jun 07 2016, Matthew Johnson <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

--
Dave

Ah, thanks, I forgot! I still consider this a bug, though (will have
to read up again what the reasons are for that behavior).

Yes, but in the case of the issue we're discussing, the choices are:

1. Omit from the existential's API any protocol requirements that depend
on Self or associated types, in which case it *can't* conform to
itself because it doesn't fulfill the requirements.

2. Erase type relationships and trap at runtime when they don't line up.

Matthew has been arguing against #2, but you can't “fix the bug” without
it.

#1 has been my preference for a while as well, at least as a starting
point.

I should point out that with the resyntaxing of existentials to
Any<Protocols...>, the idea that Collection's existential doesn't
conform to Collection becomes far less absurd than it was, so maybe this
is not so bad.

I think the problem is more that Any<Collection> does not conform to a specific value for a type parameter T: Collection

What I mean by this is that `Collection` denotes a type family, a generic parameter `T: Collection` denotes a specific (though unknown) member of that type family and `Any<Collection>` denotes the type family again, so there is really no point in writing Any<Collection> IMO.
The type family cannot conform to T because T is just one fixed member of it.
It conforms to itself, though, as I can write
let c1: Any<Collection> = …
let c2: Any<Collection> = c1

That’s why I think that we could just drop Any<Collection> and simply write Collection.

I was exploring this path a bit…

···

On Jun 9, 2016, at 7:50 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 09.06.2016 um 18:49 schrieb Dave Abrahams via swift-evolution <swift-evolution@swift.org>:
on Wed Jun 08 2016, Jordan Rose <swift-evolution@swift.org> wrote:

On Jun 8, 2016, at 13:16, Dave Abrahams via swift-evolution >>>> <swift-evolution@swift.org> wrote:
on Wed Jun 08 2016, Thorsten Seitz >>> >>>> <swift-evolution@swift.org >>>> <mailto:swift-evolution@swift.org>> >>>> wrote:

-Thorsten
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution