Making Swift more like C

Introduction

Swift is a C-family language, which means that it shares familiar syntax with other languages in that family, notably C itself. This is beneficial because it helps make Swift more accessible. Programmers who are used to working with C-family languages can generally learn new ones quite easily, and we want Swift to be easy to learn.

Motivation

To improve Swift, we should further enhance its similarities to C. Doing so will increase its familiarity to experienced programmers, which makes Swift easier to adopt for widespread use. To achieve this benefit, Swift should introduce certain idioms and operators which are available in C.

Proposed solution

Foremost and obviously, C programmers frequently use the “++” and “--” operators, as well as the three-part for loop with semicolons. The behaviors of these items are well known, and their implementations already exist in the history of the Swift repository, thus restoring them should be easy.

Furthermore, to achieve even greater similarity between the two languages, we propose adding the following constructs from C into Swift:

Inverted offset indexing

In C, not only can an array be indexed with an integer, but also an integer can be indexed with an array: 0[x] is the same as x[0]. This is simple and straightforward to implement in Swift, and we can do so in a generic, type-safe, and protocol-oriented fashion.

Of course, C uses integer offsets when indexing, whereas Swift has a more complex index model. Rather than introduce two spellings for the same thing, we propose that the inverted indexing syntax should replicate the C behavior, and thereby address the common request for offset-indexing in Swift:

extension Int {
  subscript<C: Collection> (x: C) -> C.Element {
    return x[x.index(x.startIndex, offsetBy: self)]
  }
}

Anyone coming to Swift from C who has made use of this reversed-index syntax will rejoice, as will anyone who wants “1[x]” to produce the second element of x. As a future direction, we can add a similar subscript to ranges, thus allowing “(4...7)["Hello world"]”.

Down-to loops

C-style for loops can be written like this:

for(int i = 5; i --> 0;)

As is abundantly clear from this intuitive syntax, the loop will be performed with i equal to each integer from 4 down to and including 0. The Swift equivalent can be even more elegant, allowing us to write “for i in 5 --> 0”. All we need is an operator to form a reversed range:

infix operator --> : RangeFormationPrecedence

func --> <T: Comparable> (lhs: T, rhs: T) -> ReversedCollection<Range<T>> {
  return (rhs ..< lhs).reversed()
}

This turns the compound pseudo-operator “-->” from C into a full-fledged first-class “down-to” operator in Swift. In keeping with C, it is only used for descending ranges which include the lower bound and omit the upper bound. As a future direction, we can also add the “up-to” operator “++<” that we all know and love, to form ranges which are open at the bottom and closed at the top.

Negation assignment

Who among us has not, when coding in C, mistakenly typed “=!” instead of “!=”, only to have the program compile without error, resulting in unintended behavior that subsequently manifests as a subtle and difficult to troubleshoot bug, which quite possibly isn’t detected until after the product has shipped?

Such fond memories!

We can make C afficianados feel right at home by bringing this all-too-familiar experience to Swift with a negation-assignment operator. To replicate the behavior of C, we propose adding the following:

infix operator =! : AssignmentPrecedence

func =! (lhs: inout Bool, rhs: Bool) -> Bool {
  lhs = !rhs
  return !rhs
}

func =! <T: Numeric> (lhs: inout T, rhs: T) -> Bool {
  lhs = (rhs == 0) ? 1 : 0
  return rhs == 0
}

An alternative implementation would directly inspect the bytes of any type at all, performing the implicit C-style conversion from raw memory to a boolean value. We leave that enhancement for a future follow-on proposal.

Conclusion

Making Swift more familiar to C programmers is a worthwhile goal, because it helps promote the widespread use and adoption of Swift. To improve the language in this area, we propose the addition of certain operators and functionalities that will make Swift more similar to C.

Specifically, we want new Swift programmers to be able to make exactly the same mistakes and bad design decisions that they are familiar with from C, ideally with very subtly different edge cases and pitfalls. We think this will improve the language.

14 Likes

This is a great proposal. My only suggestion is that we should support the tadpole operators as well.

prefix operator -~
prefix operator ~-

prefix func -~ <T: Numeric>(value: T) -> T {
  return value + 1
}
prefix func ~- <T: Numeric>(value: T) -> T {
  return value - 1
}

Note how easy in Swift it is to expand their domain to include floating-point numbers!

7 Likes

There is also many great idea to get from C++.

I think that the new static call proposal may use the explicit keyword, to make a value not implicitly callable. So adding a func call() on any value will make it callable, and we may opt-out this feature by using explicit.

explicit func call() {}
1 Like

Are you serious? C? C??
Imho this would be a terrible direction, which directly contradicts the first and foremost goal of Swift („Objective-C without the C“).

This proposal isn’t worth any discussion, as C is only a passing fad - we should instead focus on achieving compatibilty with FORTRAN: The Swift community could learn a lot from a truly battle-tested language, and we just need to get emojy-support into FORTRAN 2020, so that everyone feels at home right away.

6 Likes

+1! Can we add Trigraphs please? The keys for []{} etc are really hard to find!

9 Likes

U R evil!

The Swift team really needs to nail down access control before tackling this pitch. In fact, I suggest that every new proposal, regardless of subject, include an extensive supplementary section pitching a new form of access control. This will give the community more choices to choose from, which is very choicey. The more ideas for access control, the better.

3 Likes

I'm hopeful there will be other proposals bringing in some of the other obvious oversight features now that we have ABI stability. For instance, the lack of time-tested features like C's #define has often been a struggle.

I also feel that the name of the UnsafeRawPointer class gets a bit tedious - really, 'pointer' should be a clue on the sort of great power (and thus responsibility) that use of pointers supplies. I would recommend changing the name to the more terse and understandable VoidPtr, and merging it's functionality with the Any type.

2 Likes

3 Likes