[swift-evolution] [Pitch] Improving unspecified generic usability

Ross O'Brien narrativium+swift at gmail.com
Tue Aug 8 12:47:42 CDT 2017


I would like to add something to this discussion on casting generics.

I think there is a temptation to think of generic types as having a
protocol-like aspect. If String conforms to Any, then a [String] ought to
conform to [Any]; the current scope may think of the variable as being a
[Any] even though it is really a [String] and should only accept Strings as
elements. Similarly I shouldn't have to care what the element type is, if
all I want from the array is its count.

I think generic types can be complicated, particularly if they have
multiple generic components. I'm not sure that covariance and
contravariance, if I'm using those terms right, should be implicit for all
generic types.

However, I think it should be possible to explicitly make covariance or
contravariance easier to achieve for certain types.

Practically, what I'm getting at in Swift is this: 'as' and 'as?' are
keywords in Swift, but in a way they're used as operators, and it isn't
possible to write custom implementations.

For the sake of example: I am writing a Stack<Element>. It likely has an
array as a private property but it has other properties as well. I wish to
cast a Stack<String> as a Stack<Any>. I cannot; they're two different
types. The best I can do is write an init function for Stack which takes
another Stack as an argument. The initialiser will have generic constraints
because I'm using a Stack<A> as an argument to produce a Stack<B>. When
this initialiser gets called, the caller will be very clear on what type
they are declaring B to be, so the new Stack can establish its Element type.

However, the compiler would not see a problem in using a Stack<Int> as an
argument to initialising a Stack<String>. I can write code in my
initialiser, testing that each element of type A in the Stack<A> is of type
B, and I can make my initialiser failable or throws if an A not castable to
B is found, but the compiler cannot enforce the generic constraint that A :
B, or B : A. (As of Swift 4, the A : B relationship could mean that A is a
subclass of B, A conforms to B, or both.)

I would be interested in allowing the following function call as a generic
constraint, so that casting generic types can be made easier:

func convert<A, B>(from: Stack<A>) -> Stack<B> where A : B

Is this a possibility?


On Tue, Aug 8, 2017 at 2:52 PM, David Sweeris via swift-evolution <
swift-evolution at swift.org> wrote:

>
> On Aug 8, 2017, at 06:38, Karl Wagner <razielim at gmail.com> wrote:
>
>
> On 8. Aug 2017, at 04:35, David Sweeris via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>
> On Aug 7, 2017, at 3:00 PM, Logan Shire via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> One of my longstanding frustrations with generic types and protocols has
> been how hard it is to work with them when their type is unspecified.
> Often I find myself wishing that I could write a function that takes a
> generic type or protocol as a parameter, but doesn’t care what its generic
> type is.
>
> For example, if I have a type:
>
> struct Foo<T> {
>     let name: String
>     let value: T
> }
>
> or:
>
> protocol Foo {
>     associatedtype T
>     var name: String { get }
>     var value: T { get }
> }
>
> And I want to write a function that only cares about Foo.name, I’d like to
> be able to:
>
> func sayHi(to foo: Foo) {
>     print("hi \(foo.name)")
> }
>
> But instead I get the error, “Reference to generic type Foo requires
> arguments in <…>”
>
> Also, when you want to have a polymorphic array of generic types, you
> can’t:
>
> let foos: [Foo] = [Foo(name: "Int", value: 2), Foo(name: "Double", value:
> 2.0)]
>
> And if you remove the explicit type coercion, you just get [Any]
>
> let foos = [Foo(name: "Int", value: 2), Foo(name: "Double", value: 2.0)]
>
> I wish that could be inferred to be [Foo].
>
>
> What happens if you try to say "foos: [Foo<Any>] = ..."?
>
>
> Foo<Int> and Foo<Any> are very different. Otherwise, you could take a
> Foo<Int>, cast it to a Foo<Any> and set a String as its value.
>
> I think what he means are partial generics, e.g: Foo<_>.
>
>
> Oh I know, I just couldn't remember if it'd work as long as you didn't
> mess with the generic bits.
>
> - Dave Sweeris
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170808/5d7c24c2/attachment.html>


More information about the swift-evolution mailing list