[swift-evolution] [Proposal] Protocols on Steroids

Kevin Ballard kevin at sb.org
Thu Dec 31 20:12:22 CST 2015

Skimming it again, here's some brief commentary on your other suggestions:

On Wed, Dec 30, 2015, at 02:50 PM, Howard Lovatt via swift-evolution wrote:
>  1. Generic protocol with type parameters inside `<>`, like classes
>     and structs

I believe this has already been proposed.

>  1. Allow covariant return types including for generic types

I'm not sure what you mean by this. The return type of a function is
already covariant. As for generics, I'm not sure how the compiler is
supposed to know whether any other type U is a subtype of a generic type
T. In theory, we could modify Swift to allow you to say something like

func foo<T: AnyObject, U: T>(_: T.Type, _ x: U) -> T {    return x }

(right now this fails on the `U: T` because it doesn't know that T is a
class type despite the previous bound of `T: AnyObject`)

But I can't think of where this would actually be useful in practice to
do (which may be why Swift doesn't bother to support it).

>  1. Treat Self as a shorthand for the type name; in particular as
>     though there was an extra genetic type, `Type<Self: Type>` and
>     everywhere `Self` appeared in the body the compiler substitutes
>     `Type`

I suspect this has been proposed before; I know I filed a radar for this
a long time ago. Although just to be clear, what I'm thinking of is
simply the ability to use the token `Self` anywhere inside a type
definition as a convenient way to refer to the enclosing type, and not
actually changing anything about generics or type parameters.

>  1. Allow `GenericTypeName.dynamicType` to return the metatype of a
>     generic type

You can already say `T.self.dynamicType` to get the metatype.

>  1. Treat typealias as introducing a generic type parameter that is
>     not part of the type's type signature (or whatever it is
>     renamed to)

I have no idea what you mean by this. How can you have a type parameter
that's not part of the type parameters list?

>  1. Allow implementations in protocols as well as in extensions to
>     protocols (I think this is on the cards already)

This sounds not-unreasonable. Has it been proposed before?

>  1. Allow default stored properties and default inits in protocol, see
>     `Holder` example below

Your example below implies the addition of struct inheritance, which
isn't really something we can support. Inheritance implies a subtyping
relationship, but you cannot have some struct Foo be a subtype of some
other struct Bar if for no other reason than the fact that structs are
value types and so you cannot possibly substitute a value of type Foo in
any code that expects a Bar as it would be the wrong size. You could add
implicit coercions that _slice_ the value (similar to C++), but that's a
pretty nasty route to go down. Note that your example already violates
this; you can't call `printout(holder)` because a Holder<Int> is not the
same size as as Holder<Any>. Besides, you can already define that
function pretty easily with generics as `func printout<T>(value:
Holder<T>)`. I feel like all you're really trying to accomplish here is
the removal of generics from functions (since this looks awfully similar
to what you'd get with covariant mutable Arrays).

I suspect that what you really want is just a way to define common
behavior that's included in other structs, which is basically protocols
except you want to add stored properties too. So in a sense what you
really want is just a way to "embed" one struct in another (a stored
property containing a struct is a form of embedding, but I'm talking
here about embedding all its members directly without going through a
stored property). This is not a true subtyping relationship (see
previous paragraph) but accomplishes what you want.

If you want to propose such a thing, I'd suggest maybe defining it like

struct HolderInt: embeds DefaultHolder<Int> {    // ... }

to make it clear that this isn't a subtyping thing (you could make
"embeds" into a member, but that becomes a little confusing then when
you realize that the new struct acquires all of the embedded structs

That said, a fair amount of thought would have to go into doing this and
making sure it's compatible with any future changes that we want to make
to the language (it's definitely worth a proposal all on its own if you
really want to do it).

Of course, in the end your original suggestion of having a protocol with
stored properties still doesn't work, because it would be impossible to
declare protocol conformance for a type in an extension (which is a
pretty serious limitation). And I'm not really sure what benefit you
have with this over the current approach of simply having to declare the
stored property in the actual struct.

>  1. Disallow overload of a function with a generically typed argument
>     with a function whose argument is derived from the generic type,
>     must be an override.

I'm not sure what you mean by this. Can you elaborate? When you say
"derived from the generic type", it makes me think you're talking about
something like

class Foo {    func foo<T: SequenceType>(x: T) }

class Bar : Foo {    func foo<T: SequenceType>(x: T.Generator) }

But I don't understand why you'd want that second function to be marked
as an override; it's clearly _not_ an override, as it doesn't have the
same type! Note that all overridden functions can call super.func(), but
if you required the "override" keyword here you clearly can't call
super.foo() as the "overridden" function has a different type signature.

>  1. `Equatable` and co would not be a special type, could have arrays
>     of `Equatable`s

It's not a special type already, it's just a protocol. And as far as I
can tell, nothing that you've described would allow you to to have an
array of type `[Equatable]`. I suspect you're thinking that protocol
type parameters + typealiases somehow being implicit type parameters
would do this, except the type `[Equatable]` still doesn't declare the
type. Or with the typealias thing did you actually intend to have
`Equatable` look something like `Equatable<Self=T>`, thus allowing you
to say `[Equatable<Self=Int>]`? But that doesn't actually work, because
if you know that Self is an Int, then you'd just say `[Int]` instead.

An argument can be made for allowing one to specify typealiases on
existential protocol values, e.g. `GeneratorType<Element=Int>` (although
that would get pretty unwieldy when you try to figure out how to specify
a SequenceType or a CollectionType), but there's no sense in attempting
to do that for Self, because if you set Self=T then you should just use
a T directly instead of an existential protocol value.

>  1. No need for `AnyXXX` types, the protocol does this directly

No it doesn't. Barring the above-mentioned issues with specifying
typealiases on existential protocol values, types like AnySequence also
erase a lot of the specifics of the typealias. Even with your suggested
changes, there'd be no way to say "SequenceType<Element=Int>" because
that leaves the Generator and SubSequence types undefined, which means
you can't actually do much at all with the existential protocol value.
But AnySequence only has a parameter for the element type, the details
of the generator and subsequence are erased.

>  1. No need for `CollectionType` and `Array`, `Array` would become a
>     `protocol` and a `struct`

What? That doesn't make any sense. Even with your struct inheritance
idea, Array can't possibly be the "base" for all collections. If you
want a collection with the stored properties and behavior of Array, you
just use an Array! The whole point of using other collections is because
you want different behavior.

-Kevin Ballard
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151231/5abe474d/attachment.html>

More information about the swift-evolution mailing list