SE-0250: Swift Code Style Guidelines and Formatter

This point is moot; we already have a formatter called swift-format in the toolchain. It's just not as useful as it could (and should!) be.

$ swift format --help
OVERVIEW: Swift Format Tool

USAGE: swift [options] <inputs>

OPTIONS:
  -help               Display available options
  -in-place           Overwrite input file with formatted file.
  -indent-switch-case Indent cases in switch statements.
  -indent-width <n>   Number of characters to indent.
  -line-range <n:n>   <start line>:<end line>. Formats a range of lines (1-based). Can only be used with one input file.
  -o <file>           Write output to <file>
  -tab-width <n>      Width of tab character.
  -use-tabs           Use tabs for indentation.
7 Likes

If @allevato's entire team went away, my team at Apple would pick up primary ownership and ensure it gets first-class support and updates as new language grammar constructs are introduced.

19 Likes

Did it really take us 26 days and 434 posts to realize that one major goal of this proposal has actually been achieved long ago??
I wonder what this tells us about the quality of this discussion
 (or maybe it just tells me there has been to much of it to keep an overview ;-)

It definitely illustrates that being a part of the official Swift distribution doesn't help much solving the problems people actually have: As others already mentioned, the state of SPM is another indicator for this.
So imho we can't even fix the issue here on SE: It's Xcode and its inability to integrate with the tools people want to use.

3 Likes

Can we put it in stone? Is it line manager approved as this is a commitment Apple as a company is making, so monetary investment in the project that becomes a part of the roadmap.

2 Likes

Agreed: if we're already shipping a tool here, and the plan is to replace the current one (not to supplement) then the point is moot.

Good to know. :slightly_smiling_face:

In that case, while I remain in general not in favour of this particular proposal for a number of reasons elucidated over two threads, I don't think there's anything profoundly problematic with the proposal: I think it's just by and for folks with different priorities and worldviews than me.

My ideal solution would remain a project that was supported by the core team but not distributed with the language, and that had minimal or zero customisation options. However, no part of my ideal appears to have widespread support, and I'm happy enough to follow the community here.

2 Likes
  • What is your evaluation of the proposal?
    I love optional and force cast warnings in Swift, I love guard, and I love idea of code style guidelines and formatter.

  • Is the problem being addressed significant enough to warrant a change to Swift?
    Yes, this is huge problem on some big projects, especially in situations when team-lead forgot or didn't wanted to use code-style at project start.

  • Does this proposal fit well with the feel and direction of Swift?
    Yes

  • If you have used other languages with a similar feature, how do you feel that this proposal compares to those?
    I saw embedded code-style linter in Rust and I like it.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
    in-depth study

What is your evaluation of the proposal?

+1 on the idea of having an official formatter; +1 on the idea of having a default style; -1 on having an "official" style (this distinction is subtle but very important) and -1 on this proposal because it leaves me with more questions than answers and more promises than solutions.

In short, -1.

In my opinion, the proposal focuses on supporting "one true style to rule them all" and omits the importance of extensive configurability (which is mentioned just once in the whole thing).

Is the problem being addressed significant enough to warrant a change to Swift?

No, it's not, for four reasons.

1. I don't see meaningful discussions about code style that often during code review (both in open-source and closed-source projects). If there are discussions, they are quickly resolved by author either adjusting to the project style or justifying the custom style they used.

2. Swift has a well-established and well-designed grammar. Modifying coding mindset to match the project style is not a very hard thing to do.

3. Tools like SwiftLint are more than good enough solution for linting and formatting.

4. In practice, code style emerges from the architectural decisions and frameworks used. A project based on FRP framework (such as ReactiveCocoa) wouldn't and shouldn't use the same style as a project based on vanilla UIKit.

Does this proposal fit well with the feel and direction of Swift?

No, it doesn't, for two reasons.

1. Up until this proposal, Swift was marketed as an easy to learn language and expressive like no other language before.

Introducing an official style would render those two features obsolete. I don't accept the argument of it being "optional". In my opinion, thinking that there would be no pressure to adopt the official style in as many projects as possible is naive.

If this proposal is accepted, I can already see recruiters refusing to hire a developer because they don't follow the official Swift style. I can see teachers giving lower marks because there should be no space before the colon. I can see teams agreeing to ditch using a custom style in a well-argued case just because it would deviate too much from the official one.

2. This proposal is a poor proposal, in general. It doesn't present any concrete solutions to the problems it lists, especially regarding configurability. And it doesn't come with a pull request.

If you have used other languages with a similar feature, how do you feel that this proposal compares to those?

I used many tools which do linking or formatting or both: SwiftLint, ESLInt, StandardJS, Rubocop, PyLint. All those tools (except StandardJS) have one thing in common: they're configurable like crazy. This proposal doesn't remotely match that.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Read the proposal and read the pitch thread until it blew up and was impossible to follow anymore.

5 Likes

I'd like to bring attention to @minikin's insightful comment that got lost amongst the noise:

I would say a Swift version of dartfmt is exactly what I'd wish to see happen.

I especially like this passage in the formatting section of the Dart style guide:

Formatting is tedious work and is particularly time-consuming during refactoring. Fortunately, you don’t have to worry about it. We provide a sophisticated automated code formatter called dartfmt that does do it for you. We have some documentation on the rules it applies, but the official whitespace-handling rules for Dart are whatever dartfmt produces .

The remaining formatting guidelines are for the few things dartfmt cannot fix for you.

I hope we'd be able to adopt similar wording in the future.

As a Swift programmer, I only want to deal with beautifully formatted code. At the same time, I do not wish to waste any time on manually formatting code to fit into the intricate coding style that specifies how to achieve such beauty.

I want a tool that automates all aspects of code formatting.

18 Likes

+1000 to @lorentey's comment about dartfmt .

1 Like

Regarding Dart.

When you use Dart with Flutter you will have two effects. a) Hey, nice there is a tool in my IDE that just does this for me. Great lets focus on the code. b) WTF. why cant i format this huge blob of a hierarchy of components into more readable chunks so it not only satisfies the machine but also me (<insert reference about how code is 10x more read then written here>). Well, too bad i will have to live with it, Sigh.

8 Likes

Who wouldn’t agree with this? But of course beauty is in the eye of the beholder. That’s the catch. There are good reasons for people working in different contexts to have different preferences. This is especially true with regards to idioms that adopy specific formatting rules which are not applicable outside the idiom. No guidelines or formatter can handle all of these idioms gracefully because many of them have yet to be invented!

7 Likes

This is exactly the kind of issue I have been concerned about all along. If a user is fighting the formatter to breaking code into readable chunks because the formatter is not familiar with the idiom in use there is a significant problem with the tool, not the code.

A formatter should be usable with any idiom a programmer might have good reason to adopt (including new and innovative ones). This is especially the case when discussing the default behavior of a tool that ships with the language. I think any style community-wide style guidelines should be very wary of leaving room for unanticipated idioms (often very useful ones). A consequence of this is that we must whole-heartedly embrace the notion that there is no one style to rule them all.

2 Likes

There is a wide gulf between recognizing "no one style" and leaving room for an innumerable number of styles stretching to even "unanticipated" ones "yet to be invented."

Swift was designed, as the core team has said, to be an opinionated language, with a center of gravity in its design, and with a goal specifically to avoid having multiple "dialects" of the language. The tools that ship with the language should reflect that philosophy.

12 Likes

Thank you! Finally we have a (somewhat) concrete example where the standard formatter gets in the way, rather than being helpful.

I don't know anything about Flutter, but, looking at the sample code they provide, it looks like it's a DSL for building things out of nested function calls, initializers and closure expressions.

  • What exactly is wrong with dartfmt's formatting? (I.e., are they using a suboptimal typesetting algorithm, or is it a problem with the particular aesthetic style they chose to implement?)

  • Do you think Flutter’s expressions would be impossible to automatically typeset? Why?

  • Would you honestly prefer to manually format large expressions like Flutter’s deeply nested constructions?

—

Personally, I don't see a reason why it wouldn't be possible to format these expressions automatically. On the other hand, I see plenty of reasons why I wouldn’t want to manually format these -- these are exactly the sort of expressions that are annoying to typeset. I definitely would not want to laborously fix up indentation and line wrapping issues whenever I slightly change a tiny bit of a huge superstructure.

Although I don't know Flutter, I have some meaningful experience with DSLs like these -- in fact, in a previous life I worked on code that automatically pretty-prints deeply nested code like this. The underlying aesthetic principles are surprisingly(?) similar to human text; in fact, I had the best results with a straightforward adaptation of Knuth & Plass (1981).

3 Likes

I’m curious: how do you solve this exact issue right now?

It’s true that there are major readability issues with deeply nested syntactic constructs of any kind. (Expressions, statements, types, whatever.)

Consistent formatting can help mitigate these problems to an extent, so to me an automated formatter would clearly be helpful.

I don’t see how you can argue that bad code can be made better by judicious use of inconsistent formatting.

4 Likes

I’m not sure I understand the question precisely. In general I don’t choose to adopt tools which prevent me from writing the code I wish to write. I do fight with Xcode’s auto-formatting behavior from time to time. I find that annoying but it’s not a huge deal and its easy enough to tweak the code to get it formatted as necessary.

I have written code using various kinds of DSL-like syntax. When working on a new library it often takes some experimentation to identify the clearest way to format the syntax. Sometimes the result is idiosyncratic, but perfectly appropriate in context.

If this was universally true I don’t think markup languages would be as successful as they have been. I don’t think nesting is an issue when the syntax mirrors a tree structure in the domain, especially when the context is a declarative DSL. It is important to choose a layout that is appropriate to the context though.

I agree, if the formatter understands the context. If the formatter does not understand the context it will attempt to apply a set of rules that may be appropriate in general but are inappropriate in a specific context.

I am certainly not arguing this at all. I’m not sure what led you to think that I am. My argument is that style guidelines are necessarily context-dependent. Humans are able to adapt to various contexts often without even thinking about it (i.e. when writing multi-line method chains). This is much more difficult for tools, especially when you recognize that it is impossible to enumerate the idioms that will be used because new ones are invented on a not infrequent basis.

There are certainly some rules (such as left-colon-hugging) that can be applied universally. But placement and handling of line breaks and indentation are quite a bit more nuanced.

Are you aware of any formatters that exist today (for any programming language) that automatically understand context involving syntactic constructs where the particular usages differ solely by whitespace but are otherwise syntactically equivalent?

What's important isn't whether the tool understands context, but instead whether it can preserve certain kinds of context. The difference here is important; what you're asking for with the word "understanding" is for the tool to know the answer to the question "what choices can I make here and how do I choose one", when a more tractable position is for it to recognize certain formatting choices and say "the user made this choice and I recognize it as a valid one".

That goes back to the example cited in the pitch thread, where a user talked about a "function call" that can be a representation of an imperative subroutine invocation, or a markup-like DSL, or something else. Syntactically the same, but in terms of formatting, those might look quite different:

// imperative
let result = someFunction(
  someNestedFunction(oneValue, anotherValue))

// DSL
let result = someFunction(
  someNestedFunction(
    oneValue,
    anotherValue
  )
)

A formatter can recognize both of those as valid choices (for example, by respecting newlines in appropriate locations placed by the user, as swift-format does), and that's quite sufficient. It needn't be able to take the statement above written on a single line and automatically put it into the correct format, because syntactically there is not enough context for it to make that choice. It need only preserve the user's explicit intent. But it can still do that and normalize any other inconsistencies (for example, incorrect amounts of indentation within that structure) and apply fixes to any other style violations.

3 Likes
//ignore-format
let result = someFunction( 
someNestedFunction( 
oneValue,
 anotherValue ) )

can we have our cake and eat it too?

No and that is a large part of my point.

Sure, if a formatter is able to recognize user choice and leave it alone I would be fine with that. In some sense my argument is that a tool should do this by default (so it is compatible with idioms it cannot recognize and / or was not designed for). At least this is one way for a tool to be compatible with a wide range of idioms and avoid users having to fight with or configure the tool.

Of course a formatter that does this is going to produce significantly lower stylistic cohesion than a tool which aggressively reformats code. For example, I think it will be difficult to distinguish user intent from sloppy line breaks and indentation that really “should” be corrected, thus leaving some sloppiness in a codebase where it exists prior to formatting. This is going to leave a lot of users unhappy, especially those who want to be able to stop thinking about formatting altogether and have it be 100% automated. I think that’s ok - it’s certainly better than having a formatter which by default is incompatible with some common (as well as yet-to-be-invented) idioms.

I agree that this is probably the only viable approach. What I am arguing is that this preservation must be prioritized, on by default, and its performance evaluated against as wide a range of idioms as possible. Any rules that conflict with this preservation should not be enabled by default. This necessarily limits what the formatter is able to do by default.

Secondary to the tooling question, this also raises the question of how to handle style guidelines which should not be universally applied, instead leaving room for idiomatic user intent around line breaks and indentation. Ideally (IMO) the guidelines would recognize and embrace the presence of a range of idioms and state up front that any layout recommendations are necessarily specific to an idiom / context. Guidelines would probably still focus primarily or exclusively on the prevalent style used in “normal” Swift code - that’s fine as well as long as they explicitly leave room for other idioms.

5 Likes

I don't think the above is viable. A formatting tool should prioritize consistency across code bases (it's whole purpose ? ). If I want my formatting tool to implicitly read my code and infer personal preference then I probably a) should make that preference explicit (some config) so I can communicate with my team my intent or b) turn off the formatting globally, by file, or locally by some sort of commenting system like I pointed up thread or c) choose a different formatting tool?

1 Like