Major evolution of the type system, including protocol improvements, etc

I have a proposal, but it's a big one and I don't know where to post it.
I would appreciate it if someone could tell me.

The main content of this proposal, which seeks to significantly evolve Swift's type system, is as follows:

  • Improve generic data types.
  • Improve protocols and introduce a generic protocol.
  • Refine the structure of protocol declarations.
  • Use protocol qualifiers to distinguish protocol element names.
  • Change the format of protocol conformance or protocol inheritance.
  • Introduces incomplete data types and incomplete protocols.
  • Introduce an inheritance mechanism for all data types.
  • Introduce abstract types and conforming types.
  • Deprecate extensions and generic where clauses.

These proposals are expected to greatly improve program description, facilitate refactoring, and improve compiler stability.

I have a few more proposals, but for now I'll stick to the most important ones.

2 Likes

This category is a perfectly suitable place to post such content. Given the scope you describe here I suspect it would make sense to split some of this apart into separate proposals rather than trying to post a single pitch for the entire “major evolution.”

I would also personally recommend separating out fundamentally new capabilities from purely syntactic changes (if I’m correctly reading “refine the structure” and “change the format” as such?) and deprecations, which will have a higher bar for adoption and probably only serve to distract from other more important aspects in a ‘monster’ proposal.

8 Likes

thank you.

I am not good at reading and writing English.
Also, I have very little experience speaking on forums like this.
Please forgive me for not being able to explain well.

I would like to consider my proposal in several parts.

So, first of all, I would like to be ashamed of my ignorance and ask for your guidance.
What are "fundamental new features" and "pure syntactic changes"? Can you give me a concrete example to get an idea?

1 Like

Relax, your English is quite good. :slight_smile:

An example of a pure syntactic change

For example, the current syntax of a function declaration could be changed as follows.

From:

func foo () -> Bar {
   ...
}

To:

fn foo () : Bar {
   ...
}

An example of a fundamental new feature

Currently, the primitive data types such as Int and Double, unlike in C and C++, are not built-in data types. They are implemented as structs in a library.

We could introduce new built-in types for them.

For example:

let u = Z (5)    // Z is the new, built-in type for integer numbers
let v = R (2.7)  // R is the new, built-in types for real numbers.

We could also provide built-in array support for the new types.

For example:

let u : [5 x Z]       = [1, 0, 1, 1, 1]
let v : [3 x [3 x R]] = [1, 0, 0, 1, 1, 0, 1, 1, 1]
func invert (matrix u: [3 x [3 x R]]) -> [3 x [3 x R]]? {
   ...
}
2 Likes

thank you.

I am writing this with a lot of help from a translation service.

I shouldn't say that because of this,
I didn't realize that the wording I used in my question was slightly different than the original.
The original is "fundamentally new features" and "purely syntactic changes".

Aside from that,
There are also many syntax changes in my proposal.
They don't just change names and symbols, they don't just change word order, they change the very structure of sentences.
And some of them are not only syntactical changes, but integral to some basic design changes of the language.

For example in my proposal,
Change the basic definition of what a protocol is, change the basic design of the language based on that, change the syntax for protocol declarations and the syntax for using protocols.

I have to think about how to divide such a complicated proposal.

1 Like

I also think it would be great if generics would improve further, see e.g. this other topic about for-loop vs. forEach with protocols.

Just a few things I would consider:

  • You mentioned many different details. I would guess this is too large for one proposal. Maybe the community should first think where things could or should lead to, and then see which steps could lead to it.
  • I think it is very hard to make a concept from “theoretical” considerations only. What I mean is that with Swift you do not only want a very “convenient” language, but also one where you do not have to treat in much efficiency for this convenience, and also very important, performance should be predictable without surprises. I think bringing those things together is very hard.
  • Deprecating things is a difficult topic and I think not “necessary” in many cases.
  • I think purely syntactical changes would have to have a very high advantage to even be considered.

So yes, I think it would be great to have some general discussion or also some explanations from the compiler people about where the future for the type system and generics could be. (Maybe there already are some good links to share?)

I'm not good at English, so I don't fully understand the contents of the link above.

But I think it's deeply related to some of my proposals.
The relevant parts are:

  • Improve generic data types.
  • Improve protocols and introduce a generic protocol.
  • Introduces incomplete data types and incomplete protocols.

Here is an overview of these.

An incomplete data type is simply a generic data type that is used without some of its type parameters.

For example, we have some generic data type with two type parameters.
When using this, of course you need to give the two type parameters some concrete type.
This is the act of generating a normal, complete data type.

Consider using one or more type parameters without specifying a specific type.
This is the act of generating an incomplete data type.

Constituents of generic data types will have properties and methods referencing type parameters to interfaces and implementations.
An incomplete data type cannot refer to a type parameter that has not been given a concrete type.
As a result, some interfaces are no longer available, and so are all implementations.
This is similar to the protocol with PAT in Swift today, an abstract type to refer to some complete data type.
I think it's quite convenient because it eliminates the need to create parameter-erased data types such as AnyKeyPath and PartialKeyPath one by one.
Using the incomplete data type requires a small syntactic change from using the generic data type.

struct S<T1, T2> {
    var t1: T1
    var t2: T2
    init(t1: T1, t2: T2) { ... }
}

// Complete data type

let s = S<T1: Int, T2: String>(t1: 0, t2: "foo")

// Incomplete data types

var s1: S<T1: Int> = s
s1.t1 = 1
// s1 does not have property t2.

var s2: S<T2: String> = s
s2.t2 = "bar"
// s2 does not have property t1.

Consider parameterizing protocol association types in the same way as generic data types.
The result is a generic protocol.
Incomplete protocols can be generated from generic protocols as well as generic data types.
The syntax for using the generic protocol is slightly different than the syntax for using the protocol with PAT.

// Current definition style

protocol P {
    associatedtype T1
    associatedtype T2
    var t1: T1 { get set }
    var t2: T2 { get set }
}

// New definition style
// This is the generic protocol.
// "<T1, T2>" is not a PAT definition, it is an associated type parameter definition.

protocol P<T1, T2> {
    var t1: T1 { get set }
    var t2: T2 { get set }
}

// Here we assume that S conforms to P.
let s = S<T1: Int, T2: String>(t1: 0, t2: "foo")

// Complete protocol

var p: P<T1: Int, T2: String> = s
p.t1 = 1
p.t2 = "bar"

// Incomplete protocols

var p1: P<T1: Int> = s
p1.t1 = 1
// p1 does not have property t2.

var p2: P<T2: String> = s
p2.t2 = "bar"
// p2 does not have property t1.

The example above is transitional, as I propose other changes to the protocol.

1 Like

Just to protect you from later disappointment:
It is nearly impossible to successfully pitch a major change of the language in this forum.
So I'd rather pick some very tiny improvement that can stand on its own, like adding some convenient method to one of the types in the stdlib.
Such a change has the additional benefit that it can be turned into a proposal much more easily, because you don't have to mess with the C++ internals to drive the idea forward.

1 Like

Thank you for your kind words.

My proposal might be as bold as messing around with the internals of the compiler, or rebuilding the whole thing.
Moreover, most of the proposals are the product of my imagination alone.
It wasn't thought through enough detail, so you may find some glaring flaws in the discussion process.

I may be selfish, but I think this is a groundbreaking idea that makes Swift really useful.
Even if it is impossible to incorporate it into Swift now, is it possible to discuss it with the expectation that it may be useful in the future?

1 Like

Before deciding how to divide the proposals, I would like to discuss the most important ones for now.

It is a change in the definition of what a protocol is and various changes that accompany it.
But with that said, it's hard for me to explain what a protocol is.

Therefore, I would like to put this on the back burner and explain how I would like to change the definition of the protocol.

First, the current protocol positions an associated type as something that conforming data types should define within it.
for example,

protocol P {
    associatedtype T
}

struct S: P {
    typealias T = Int // associated type T is defined here
}

I think this is where it gets really tricky.
I want to change it to:

protocol P<T> { }

struct S: P<T: Int> { }

It turns an associated type into a parameter of a protocol call.
When calling, describe the parameters with the parameter label.
See here for reasons to do this.

The above example simply shows what the format looks like when the data type conforms to the protocol.
A slightly more complex example, if the generic data type conforms to this protocol, could be written as:

struct S<T>: P<T: T> { }

The formal argument of S can be set to the actual argument of P.

In the case of protocol inheritance, it is written as follows.

protocol P1<T1> { }
protocol P2<T2> { }

protocol P3<T1, T2>: P1<T1: T1>, P2<T2: T2> { }

In the examples so far, the associated type has no type constraint, but it can of course.

protocol P1<T1: Equatable> { }

protocol P2<T2: Comparable>: P1<T1: T2> { }

It's a very rough explanation, but doesn't it look cleaner and easier to understand when the syntax changes like this?

This syntax eliminates the need to write a generic where clause, at least in protocol and data type definitions.

Wouldn't the internals of the compiler be cleaner and easier to understand?

I have more ideas for the protocol, but I'll take a break here.

2 Likes

From here I will explain protocol qualifiers.

Consider the following two protocols:

protocol P1 {
     associatedtype Item
     var description: String { get }
}

protocol P2 {
     associatedtype Item
     var description: String { get }
}

P1 and P2 have an associated type with the same name Item and a property with the same name description.
Data types conforming to these two protocols are defined as follows.

struct S: P1, P2 {
     typealias Item = Int
     var description: String = "foo"
}

Item in P1 and Item in P2 will have the same data type.
description of P1 and description of P2 will have the same entity.
The same is true for methods instead of properties.
I think this is the current Swift spec, is this the way it should be?
I think it's always possible to have a situation where you want to give each protocol a different data type or property entity, but you can't because they have the same name.
As it stands, I think they try to use names that are unlikely to match others to avoid this problem.

The example I just gave is a problem between protocols trying to conform to it at the same time, but it also happens when you inherit from one protocol and define another.
In this case too, we have to avoid unintended name matching, so for example, give a slightly longer name as shown below.

protocol P1 {
     associatedtype P1Item
     var p1Description: String { get }
}

protocol P2: P1 {
     associatedtype P2Item
     var p2Description: String { get }
}

Although story changes,
Many delegation protocols unify all method names with the protocol name.
I think the purpose of doing so is to avoid name clashes with other protocols and to make it easier to identify which protocol it belongs to from the method name.
The various delegation protocol methods of UITableView, which I often use, have a well-thought-out interface, and I think it's amazing, but I think it's hard for me to define an interface like this myself.

Despite the lengthy preamble, these considerations gave me a good idea.
One is the idea of associated types, already mentioned here.
This completely prevents associated type name collisions.
I will proceed on the premise that I have adopted this idea.
Another is protocol modifiers.
It is used in front of protocol properties and methods.
To give a simple example,

protocol P1<Item> {
     var description: String { get }
}
protocol P2<Item> {
     var description: String { get }
}

struct S: P1<Item: Int>, P2<Item: String> {
     var P1::description: String = "foo" // Attention here!
     var P2::description: String = "foo" // Attention here!
}

Properties and methods defined in a protocol should be used in pairs with the protocol name.
The delimiter "::" is the same symbol as the scope resolution operator in C++, but in Swift it is only used to give the protocol name.

By doing this, the protocol-related properties and methods are always written in pairs with the protocol name, so I think it will improve the readability of the program.
Property names and method interfaces can also be more concise.
If the protocol name makes the description rather long, you can also use a short name typealias that refers to the protocol.

The overview of protocol modifiers ends here.

Next, I'd like to explain my ideas for changing protocol definitions and default implementations of requirement elements.

1 Like

Are you familiar with primary associated types of protocols? A feature similar to what you suggest was introduced in Swift 5.7. (Link for Japanese article on primary associated types)

Thank you for your advice.

I am surprised and grateful that the information on the link you gave me is in Japanese and I can understand it properly.

I think I have a fair understanding of what you are pointing out.
The PAT released in 5.7 is a combination of associated type parameters determined at the protocol definition stage, and only one can be defined.
What I'm proposing is that any combination is acceptable to the compiler, and therefore doesn't need to be decided at the definition stage.

1 Like

Welcome to the Swift forums, @YoshihiroUnno! It's always wonderful to see someone who is enthusiastic about improving the language.

It's important to keep in mind that the Swift language is in active use by lots of people; as such, changing the "basic design of the language" would not be possible, since we're not creating a new programming language from scratch.

Some of the ideas you're presenting are intriguing but seem like they would be major engineering projects even to create a proof-of-concept. Keep this in mind as you work on any proposal, since a proposal isn't reviewable without at least a substantially complete implementation. Therefore, if what you propose would require many years of engineering time, it wouldn't be possible to get it to a point that it can be reviewed.

1 Like

i concur with Tino here, it is highly unlikely that this proposal will be successful.

you can learn this the easy way by listening to folks who have been around here for a very long time, or you can learn this the hard way from spending a ton of effort designing an overhaul of the language and then getting stonewalled, but the inconvenient truth is major changes to the type system that don't originate from a handful of highly recognizable Apple experts stand virtually zero chance of passage.

it is possible to become a highly recognizable Apple expert by getting a job at Swift at Apple and maneuvering your way into this decision-making group - i can recall at least one individual who has successfully achieved this over the past 4 or 5 years despite starting “in the pigeonhole” at Apple, so to speak. but it's a challenging career goal that requires ambition and some amount of institutional smarts and not something that can be done by just having good ideas and pitching good designs.

To be clear, radically changing the type system, as opposed to extending it, is unlikely even for a "highly recognizable Apple expert". Swift is a nine-year-old language with conservatively well over a million programmers. There is still some room for disruptive changes with e.g. new language compatibility versions, but the benefits of those changes will have to be balanced against the scale of the disruption, and that balance will almost always come out in favor of not breaking existing code.

21 Likes

for curiosity’s sake, where are you getting this number from?

2 Likes

There are several different public estimates, such as the Evans and SlashData reports, or you can try to work backwards from iOS developer figures. I feel pretty confident in 1 million as a lower bound.

3 Likes

I don't want to keep picking on this post, but I do need to explicitly counter what might have been an inadvertent implication here. There have been quite a few significant features contributed by non-Apple developers, including SE-0309, which is a fundamental type-system feature. Most features like that come from Apple engineers simply because Apple pays a lot of people to work full-time on Swift, not because the project is trying to block or downgrade non-Apple contributions.

23 Likes

What are these? Dialects?