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

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

···

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

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> 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:
>>>>>
>>>>
>>>>>>> , 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 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

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.

···

on Wed Jun 08 2016, Matthew Johnson <matthew-AT-anandabits.com> 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

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.

···

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

-Thorsten

Am 08.06.2016 um 21:43 schrieb Austin Zheng <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>> >> wrote:

> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution >> > <swift-evolution@swift.org
> <mailto:swift-evolution@swift.org>>:
>
>
> 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 >> >>> <mailto:dabrahams@apple.com>> >> >>> wrote:
>>>
>>>
>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com >> >>> <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:
>>>>>
>>>>
>>>>>>> , 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>
> 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
<https://lists.swift.org/mailman/listinfo/swift-evolution&gt;

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

--
Dave

#1 has been my preference for a while as well, at least as a starting point. It's possible we could also "open" the existential when it's only used by one parameter, i.e. the first would be legal and the second wouldn't:

func foo<X: Hashable>(x: X) { … }
func test(x: Any<Hashable>) {
  foo(x) // okay, passes the dynamic type
}

func bar<X: Hashable>(a: X, b: X) { … }
func test(x: Any<Hashable>, y: Any<Hashable>) {
  bar(x, y) // illegal because x.dynamicType may be different from y.dynamicType
}

(The check is not as simple as "the generic parameter is only mentioned once", because of constraints and such. But you get the idea.)

Jordan

···

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.

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] {

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> wrote:

Sent from my iPad

> On Jun 8, 2016, at 3:16 PM, Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:
>
>
>> on Wed Jun 08 2016, Thorsten Seitz <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>:
>>>
>>> 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>> > >>> wrote:
>>>
>>>> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution > >>>> <swift-evolution@swift.org
>>>> <mailto:swift-evolution@swift.org>>:
>>>>
>>>>
>>>> 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 > >>>>>> <mailto:dabrahams@apple.com>> > >>>>>> wrote:
>>>>>>
>>>>>>
>>>>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com > >>>>>> <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:
>>>>>>>
>>>>>>>>>> , 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>
>>>> 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
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> --
> 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

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.

Best,
Austin

···

On Wed, Jun 8, 2016 at 2:37 PM, Austin Zheng <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] {
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] {
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> wrote:

Sent from my iPad

> On Jun 8, 2016, at 3:16 PM, Dave Abrahams via swift-evolution < >> swift-evolution@swift.org> wrote:
>
>
>> on Wed Jun 08 2016, Thorsten Seitz <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>:
>>>
>>> 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>> >> >>> wrote:
>>>
>>>> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution >> >>>> <swift-evolution@swift.org
>>>> <mailto:swift-evolution@swift.org>>:
>>>>
>>>>
>>>> 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 >> >>>>>> <mailto:dabrahams@apple.com>> >> >>>>>> wrote:
>>>>>>
>>>>>>
>>>>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com >> >>>>>> <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:
>>>>>>>
>>>>>>>>>> , 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>
>>>> 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
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> --
> 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

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.

But as I've been suggesting, #1 may not worth generalizing existentials
for, especially considering that Doug says by a factor of 10x people who
want parameterized protocols are asking for things like Collection<T>.

It's possible we could also "open" the existential when it's only used
by one parameter, i.e. the first would be legal and the second
wouldn't:

func foo<X: Hashable>(x: X) { … }
func test(x: Any<Hashable>) {
  foo(x) // okay, passes the dynamic type
}

func bar<X: Hashable>(a: X, b: X) { … }
func test(x: Any<Hashable>, y: Any<Hashable>) {
  bar(x, y) // illegal because x.dynamicType may be different from y.dynamicType
}

(The check is not as simple as "the generic parameter is only
mentioned once", because of constraints and such. But you get the
idea.)

Interesting from a “completing the type system” perspective but perhaps
not very useful. What are the use cases for this? What you can do
with Hashable is intimately dependent on the type relationship.

We should really stop using protocols refining Equatable as an exemplar,
because that's a special case that has a special answer when the types
don't match up**. It's not representative of the general case, though.

** and any generalized Existential system we implement ought to support
   that answer!

···

on Wed Jun 08 2016, Jordan Rose <jordan_rose-AT-apple.com> 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

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):

[Ugh, Austin, your mail program is stripping the tabs out of the
plaintext part so the indendation is lost. Grabbing from browser...]

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
    }
  }
}

Presumably this would “work” as well?

  typealias IntSequence = Any<Sequence where .Iterator.Element == Int>
  func f(x: IntSequence, y: IntSequence) {
    var i = x.makeIterator()
    i = y.makeIterator() // <== NO TRAP HERE, EVER.
  }

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
}

Sure, this one is simple because the associated type is never even
exposed.

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)

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.

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)

···

on Wed Jun 08 2016, Austin Zheng <austinzheng-AT-gmail.com> wrote:

--
Dave

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.

Don't you mean #2? Otherwise I'm confused. #1 is the one that
prohibits more usages.

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.

Not if you consider Any<Collection where Element == Int> to be a
concrete type. Concrete w.r.t. to a generic parameter means something
different from Concrete w.r.t. subtyping.

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.

I'd really like to see the use-cases, to make sure that these restricted
existentials will be useful enough to be worth implementing.

···

on Wed Jun 08 2016, Austin Zheng <austinzheng-AT-gmail.com> wrote:

Best,
Austin

On Wed, Jun 8, 2016 at 2:37 PM, Austin Zheng <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] {
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] {
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> wrote:

Sent from my iPad

> On Jun 8, 2016, at 3:16 PM, Dave Abrahams via swift-evolution < >>> swift-evolution@swift.org> wrote:
>
>
>> on Wed Jun 08 2016, Thorsten Seitz <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>:
>>>
>>> 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>> >>> >>> wrote:
>>>
>>>> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution >>> >>>> <swift-evolution@swift.org
>>>> <mailto:swift-evolution@swift.org>>:
>>>>
>>>>
>>>> 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 >>> >>>>>> <mailto:dabrahams@apple.com>> >>> >>>>>> wrote:
>>>>>>
>>>>>>
>>>>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com >>> >>>>>> <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:
>>>>>>>
>>>>>>>>>> , 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>
>>>> 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
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> --
> 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

--
Dave

(inline)

> 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):

[Ugh, Austin, your mail program is stripping the tabs out of the
plaintext part so the indendation is lost. Grabbing from browser...]

Sorry! I'm using gmail in the browser. I'll stay away from tabs...

> func copySequenceIntoArray(x: Any<Sequence where .Iterator.Element ==
>) -> [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
> }
> }
> }

Presumably this would “work” as well?

  typealias IntSequence = Any<Sequence where .Iterator.Element == Int>
  func f(x: IntSequence, y: IntSequence) {
    var i = x.makeIterator()
    i = y.makeIterator() // <== NO TRAP HERE, EVER.
  }

This presumably wouldn't compile. The sort-of-dependent-type "x.Iterator"
(return value of x.makeIterator(); inferred as type of 'i'), and
"y.Iterator" (return type of y.makeIterator()) would not be considered
equivalent.

(As Doug mentioned in a recent email, it would only be feasible to expose
such types on immutable values. The intention is not to allow the
largely-meaningless code that follows:

func f(x: IntSequence, y: IntSequence) {
  var z : IntSequence = x
  var something : z.Iterator = x.makeIterator()
  z = y
  something = y.makeIterator()
  // ...
}
)

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

Sure, this one is simple because the associated type is never even
exposed.

> 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)
>
> 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.

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

Of course. I don't know what the right solution to this is yet.

···

On Wed, Jun 8, 2016 at 3:18 PM, Dave Abrahams <dabrahams@apple.com> wrote:

on Wed Jun 08 2016, Austin Zheng <austinzheng-AT-gmail.com> wrote:

  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

> 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.

Don't you mean #2? Otherwise I'm confused. #1 is the one that
prohibits more usages.

I'm just being cautious until a better solution comes along.

> 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.

Not if you consider Any<Collection where Element == Int> to be a
concrete type. Concrete w.r.t. to a generic parameter means something
different from Concrete w.r.t. subtyping.

You can consider Any<Collection where .Element == Int> to be a concrete
type, but then you have the unprecedented situation where the associated
types associated with a concrete type aren't necessarily the same for all
instances (is this true for any type that can satisfy a generic type
parameter today?).

(For the sake of this argument, Array isn't a concrete type, but Array<T>
or Array<Int> is. You can't use 'Array' anywhere in Swift today, so I think
my assertion is fair.)

My understanding is that fixing the generic type parameter T by
specializing a function/type also fixes all the associated types associated
with T. This 'contract' would have to be weakened to allow existential
types to satisfy generic type parameters in any non-trivial way.

> 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.

I'd really like to see the use-cases, to make sure that these restricted
existentials will be useful enough to be worth implementing.

This is enormously important.

First of all, (of course you know) there is a semantic difference between:

protocol Pet : class { }; class Cat : Pet { }; class Dog : Pet { }

class AnimalShelter<T : Pet> { var pet: T }

and

class AnimalShelter { var pet: Pet }

This is something you can express for simple existentials today (the code
above should run, with minor modifications), but not for existentials
containing associated types or self requirements.

I think a big part of what people want to do is to declare variables, pass
args to functions, and get return values from functions that abstract over
something like Collection. They want to do this without having to make
their code generic, and without forcing their code to be homogenous at
runtime (e.g. an instance of the dynamic AnimalShelter can be populated
with a cat and later a dog, but the generic one can only ever contain
either; extend this to Collections of Ints or whatnot).

The big problem is that existentials can't guarantee that they satisfy the
contract generic functions and types are expecting, as we've been
discussing.

To be honest, I think requiring existentials to be opened should be a
prerequisite to using them generically. This would define away the
impedance mismatch at the expense of making certain things marginally
harder to do. (An alternate way of thinking about it is that it would make
the potential for a runtime error explicit, and localize it):

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

let a : Any<Collection where .Element == Int>
let b : Any<Collection where .Element == Int>

// Prohibit this...
// doSomething(a, b)

// Allow this:
if let a = a openas? T, b = b as? T {
  // We've recovered the 'strong guarantee' that doSomething expects for T
  doSomething(a, b)
} else {
  // Here's our trap
}

The biggest problem is that this sort of solution would prohibit
existentials from being used in generic contexts where the existentials
only have to be "similar enough", not identical, for things to work out.
Given how fuzzy "similar enough" has proven to be, maybe that's not a
terrible tradeoff.

···

On Wed, Jun 8, 2016 at 3:22 PM, Dave Abrahams <dabrahams@apple.com> wrote:

on Wed Jun 08 2016, Austin Zheng <austinzheng-AT-gmail.com> wrote:

>
>
> Best,
> Austin
>
> On Wed, Jun 8, 2016 at 2:37 PM, Austin Zheng <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] {
>> 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] {
>> 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> wrote:
>>
>>>
>>>
>>> Sent from my iPad
>>>
>>> > On Jun 8, 2016, at 3:16 PM, Dave Abrahams via swift-evolution < > >>> swift-evolution@swift.org> wrote:
>>> >
>>> >
>>> >> on Wed Jun 08 2016, Thorsten Seitz <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 > >:
>>> >>>
>>> >>> 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>> > >>> >>> wrote:
>>> >>>
>>> >>>> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution > >>> >>>> <swift-evolution@swift.org
>>> >>>> <mailto:swift-evolution@swift.org>>:
>>> >>>>
>>> >>>>
>>> >>>> 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 > >>> >>>>>> <mailto:dabrahams@apple.com>> > >>> >>>>>> wrote:
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com > >>> >>>>>> <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:
>>> >>>>>>>
>>> >>>>>>>>>> , 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>
>>> >>>> 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
>>> >>> <https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
>>> >>
>>> >> _______________________________________________
>>> >> swift-evolution mailing list
>>> >> swift-evolution@swift.org
>>> >> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> >
>>> > --
>>> > 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
>>>
>>
>>

--
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.

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.

···

Sent from my iPad

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

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

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>:

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>> >>> wrote:

Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution >>>> <swift-evolution@swift.org
<mailto:swift-evolution@swift.org>>:

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 >>>>>> <mailto:dabrahams@apple.com>> >>>>>> wrote:

on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com >>>>>> <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:

, 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>
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
<https://lists.swift.org/mailman/listinfo/swift-evolution&gt;

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

--
Dave

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

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.

···

On Jun 8, 2016, at 4:47 PM, Austin Zheng <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> 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/ > >>>>>> <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

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.

···

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 <mailto: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 <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>>:
>
>
> 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:
>>>>>
>>>>
>>>>>>> , 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>
> 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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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_.

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.

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

···

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

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

> 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.

Don't you mean #2? Otherwise I'm confused. #1 is the one that
prohibits more usages.

I'm just being cautious until a better solution comes along.

> 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.

Not if you consider Any<Collection where Element == Int> to be a
concrete type. Concrete w.r.t. to a generic parameter means something
different from Concrete w.r.t. subtyping.

You can consider Any<Collection where .Element == Int> to be a concrete type, but then you have the unprecedented situation where the associated types associated with a concrete type aren't necessarily the same for all instances (is this true for any type that can satisfy a generic type parameter today?).

(For the sake of this argument, Array isn't a concrete type, but Array<T> or Array<Int> is. You can't use 'Array' anywhere in Swift today, so I think my assertion is fair.)

My understanding is that fixing the generic type parameter T by specializing a function/type also fixes all the associated types associated with T. This 'contract' would have to be weakened to allow existential types to satisfy generic type parameters in any non-trivial way.

> 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.

I'd really like to see the use-cases, to make sure that these restricted
existentials will be useful enough to be worth implementing.

This is enormously important.

First of all, (of course you know) there is a semantic difference between:

protocol Pet : class { }; class Cat : Pet { }; class Dog : Pet { }

class AnimalShelter<T : Pet> { var pet: T }

and

class AnimalShelter { var pet: Pet }

This is something you can express for simple existentials today (the code above should run, with minor modifications), but not for existentials containing associated types or self requirements.

I think a big part of what people want to do is to declare variables, pass args to functions, and get return values from functions that abstract over something like Collection. They want to do this without having to make their code generic, and without forcing their code to be homogenous at runtime (e.g. an instance of the dynamic AnimalShelter can be populated with a cat and later a dog, but the generic one can only ever contain either; extend this to Collections of Ints or whatnot).

The big problem is that existentials can't guarantee that they satisfy the contract generic functions and types are expecting, as we've been discussing.

To be honest, I think requiring existentials to be opened should be a prerequisite to using them generically. This would define away the impedance mismatch at the expense of making certain things marginally harder to do. (An alternate way of thinking about it is that it would make the potential for a runtime error explicit, and localize it):

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

let a : Any<Collection where .Element == Int>
let b : Any<Collection where .Element == Int>

// Prohibit this...
// doSomething(a, b)

// Allow this:
if let a = a openas? T, b = b as? T {
  // We've recovered the 'strong guarantee' that doSomething expects for T
  doSomething(a, b)
} else {
  // Here's our trap
}

Right... exactly what I wrote yesterday, from Doug having written it months ago.

You are still missing the dynamicType checking to make invoking comparable{} methods work.

and that could further be simplified with something like

If letAs a:T , y:T {
   // compatible types, but still possible for dynamic types to be different
}

LetAs is a strawman for
    Let a = a openas? T

···

On Jun 9, 2016, at 12:49 AM, Austin Zheng via swift-evolution <swift-evolution@swift.org> wrote:

On Wed, Jun 8, 2016 at 3:22 PM, Dave Abrahams <dabrahams@apple.com> wrote:
on Wed Jun 08 2016, Austin Zheng <austinzheng-AT-gmail.com> wrote:

The biggest problem is that this sort of solution would prohibit existentials from being used in generic contexts where the existentials only have to be "similar enough", not identical, for things to work out. Given how fuzzy "similar enough" has proven to be, maybe that's not a terrible tradeoff.

>
>
> Best,
> Austin
>
> On Wed, Jun 8, 2016 at 2:37 PM, Austin Zheng <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] {
>> 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] {
>> 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> wrote:
>>
>>>
>>>
>>> Sent from my iPad
>>>
>>> > On Jun 8, 2016, at 3:16 PM, Dave Abrahams via swift-evolution < >> >>> swift-evolution@swift.org> wrote:
>>> >
>>> >
>>> >> on Wed Jun 08 2016, Thorsten Seitz <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>:
>>> >>>
>>> >>> 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>> >> >>> >>> wrote:
>>> >>>
>>> >>>> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution >> >>> >>>> <swift-evolution@swift.org
>>> >>>> <mailto:swift-evolution@swift.org>>:
>>> >>>>
>>> >>>>
>>> >>>> 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 >> >>> >>>>>> <mailto:dabrahams@apple.com>> >> >>> >>>>>> wrote:
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com >> >>> >>>>>> <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:
>>> >>>>>>>
>>> >>>>>>>>>> , 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>
>>> >>>> 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
>>> >>> <https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
>>> >>
>>> >> _______________________________________________
>>> >> swift-evolution mailing list
>>> >> swift-evolution@swift.org
>>> >> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> >
>>> > --
>>> > 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
>>>
>>
>>

--
Dave

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

> 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.

Don't you mean #2? Otherwise I'm confused. #1 is the one that
prohibits more usages.

I'm just being cautious until a better solution comes along.

> 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.

Not if you consider Any<Collection where Element == Int> to be a
concrete type. Concrete w.r.t. to a generic parameter means something
different from Concrete w.r.t. subtyping.

You can consider Any<Collection where .Element == Int> to be a concrete
type, but then you have the unprecedented situation where the associated
types associated with a concrete type aren't necessarily the same for all
instances (is this true for any type that can satisfy a generic type
parameter today?).

(For the sake of this argument, Array isn't a concrete type, but Array<T>
or Array<Int> is. You can't use 'Array' anywhere in Swift today, so I think
my assertion is fair.)

My understanding is that fixing the generic type parameter T by
specializing a function/type also fixes all the associated types associated
with T. This 'contract' would have to be weakened to allow existential
types to satisfy generic type parameters in any non-trivial way.

> 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.

I'd really like to see the use-cases, to make sure that these restricted
existentials will be useful enough to be worth implementing.

This is enormously important.

First of all, (of course you know) there is a semantic difference between:

protocol Pet : class { }; class Cat : Pet { }; class Dog : Pet { }

class AnimalShelter<T : Pet> { var pet: T }

and

class AnimalShelter { var pet: Pet }

This is something you can express for simple existentials today (the code
above should run, with minor modifications), but not for existentials
containing associated types or self requirements.

Sorry, what code? The above code runs just fine. Maybe you mean

   typealias T = AnimalShelter<Pet>

?

I think a big part of what people want to do is to declare variables, pass
args to functions, and get return values from functions that abstract over
something like Collection. They want to do this without having to make
their code generic, and without forcing their code to be homogenous at
runtime (e.g. an instance of the dynamic AnimalShelter can be populated
with a cat and later a dog, but the generic one can only ever contain
either; extend this to Collections of Ints or whatnot).

The big problem is that existentials can't guarantee that they satisfy the
contract generic functions and types are expecting, as we've been
discussing.

To be honest, I think requiring existentials to be opened should be a
prerequisite to using them generically.

It's a reasonable approach.

This would define away the impedance mismatch at the expense of making
certain things marginally harder to do. (An alternate way of thinking
about it is that it would make the potential for a runtime error
explicit, and localize it):

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

let a : Any<Collection where .Element == Int>
let b : Any<Collection where .Element == Int>

// Prohibit this...
// doSomething(a, b)

// Allow this:
if let a = a openas? T, b = b as? T {
  // We've recovered the 'strong guarantee' that doSomething expects for T
  doSomething(a, b)
} else {
  // Here's our trap
}

The biggest problem is that this sort of solution would prohibit
existentials from being used in generic contexts where the existentials
only have to be "similar enough", not identical, for things to work out.
Given how fuzzy "similar enough" has proven to be, maybe that's not a
terrible tradeoff.

Maybe not. More info is needed, is all I'm saying.

···

on Wed Jun 08 2016, Austin Zheng <austinzheng-AT-gmail.com> wrote:

On Wed, Jun 8, 2016 at 3:22 PM, Dave Abrahams <dabrahams@apple.com> wrote:

on Wed Jun 08 2016, Austin Zheng <austinzheng-AT-gmail.com> wrote:

>
>
> Best,
> Austin
>
> On Wed, Jun 8, 2016 at 2:37 PM, Austin Zheng <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] {
>> 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] {
>> 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> wrote:
>>
>>>
>>>
>>> Sent from my iPad
>>>
>>> > On Jun 8, 2016, at 3:16 PM, Dave Abrahams via swift-evolution < >> >>> swift-evolution@swift.org> wrote:
>>> >
>>> >
>>> >> on Wed Jun 08 2016, Thorsten Seitz <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 >> >:
>>> >>>
>>> >>> 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>> >> >>> >>> wrote:
>>> >>>
>>> >>>> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution >> >>> >>>> <swift-evolution@swift.org
>>> >>>> <mailto:swift-evolution@swift.org>>:
>>> >>>>
>>> >>>>
>>> >>>> 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 >> >>> >>>>>> <mailto:dabrahams@apple.com>> >> >>> >>>>>> wrote:
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com >> >>> >>>>>> <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:
>>> >>>>>>>
>>> >>>>>>>>>> , 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>
>>> >>>> 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
>>> >>> <https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
>>> >>
>>> >> _______________________________________________
>>> >> swift-evolution mailing list
>>> >> swift-evolution@swift.org
>>> >> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> >
>>> > --
>>> > 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
>>>
>>
>>

--
Dave

--
Dave

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.

···

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.

--
Dave

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 9, 2016, at 8:06 AM, Austin Zheng <austinzheng@gmail.com> wrote:

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> 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:
>>>>>
>>>>
>>>>>>> , 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/:

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.

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? 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.

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

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. 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. 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.

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. :)

···

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:

--
Dave