Since I started programming in Swift, I moved to the “Ash Rule” of left-hugging colons in all uses except ternary, up to and including dictionary references, protocols, etc. Here’s what that looks like in use:
let x: [String: String] = ["key": "value"] let y = foo(param1: value1, param2: value2) func bar<T: Hashable>(a: T) -> Void {}
It felt weird for a while but I’ve now grown used to it. This approach is consistent, prioritizes the role of the left item, and is (in my opinion) quite readable. Compare and contrast with my prior style, which I call the “Full Space Monty” approach:
let x : [String : String] = ["key" : "value"] let y = foo(param1 : value1, param2 : value2) func bar<T : Hashable>(a : T) -> Void {}
The Ash Rule puts me in step with about half of Apple source code and out of step with the rest. Colon magnetism tends to vary by both group and individual producer. I mention this because Dave Abrahams clarified StdLib style on the Swift Evolution list today:
- Full monty spaces for declarations and extensions
- Left magnetism for providing values
Here’s an example I grabbed from Evolution that showcases some fascinating code styling choices. Focusing only on colons however, here’s what there is:
public func contains(value: Bound) -> Bool public var description: String protocol _ClosedRange : _RangeProtocol {} public struct ClosedRangeIndex< Bound : Comparable where Bound : _Strideable > : Comparable {
I’m going to assume the bolded use in the following snippet is an oversight:
extension _ClosedRange { @inline(__always) public init< Other: _ClosedRange where Other.Bound == Bound >(_ other: Other) { self.init( _uncheckedBounds: (lower: other.lowerBound, upper: other.upperBound) ) } ... }
While this next one is intentional since the ClosedRangeIndex is typing the lhs and rhs arguments:
public func == <B>(lhs: ClosedRangeIndex<B>, rhs: ClosedRangeIndex<B>) -> Bool {
This style isn’t universal. Apple Developer Publications uses Mike Ash colons.
When it comes to colon style, I don’t think there are right or wrong answers but I do think that whatever style you do choose naturally breaks down into the following categories:
- label declarations func f(x: T)
- collection lookups [key: value] (Empty case is universally [:] not [: ].)
- cases case A, B:
- inheritance class x: Y
- conformance <T: A>, <T: protocol<A, B>> {}
- attribute @attribute(key: value), e.g. @available(*, unavailable, renamed: “MyRenamed”)
- ternary A ? B : C
I think there’s a general unity of opinion when it comes to case
and ternary. No space for cases; always spaces for ternary. There’s little controversy or variation as far as I can tell.
The newly attribute style is up for grabs but I think it will follow label declarations in using left-magnetic colons.
The Swift Programming Language uses left-magnetic colons for dictionaries:
although the grammar does not: (Jordan Rose: ” Not sure I’d count the grammar as any particular evidence. Traditionally, grammars put spaces around everything.”)
Its property, constant, and variable declarations are pretty much uniformly left-magnetic:
I didn’t find any inheritance that wasn’t left-magnetic:
So what’s your house style and how did you come to it?
11 Comments
Left magnet except for ternary. Left magnet looks more natural since that’s how you’d use it in English sentences. Just like commas.
I follow the same as you, but I normally don’t put a space after the colon. The biggest style issue for me is putting opening brackets on the end of a line that will wrap. I can scan source much faster if func opening braces are left aligned on the following line.
I greet you fellow Allman aficionado
I settled early on in using left magnet in all cases except ternary. I find it easier to read this style vs. full monty spacing. The : in a ternary operator should have full monty spacing (even though not required) to achieve visual balance and improve readability, because the ? is _required_ to have a space (otherwise, the ? is regarded as a postfix optional operator). Although the StdLib does consistently use full monty spacing for declarations and extensions, I couldn’t find Dave Abrahams clarification of this style guideline. Can you provide a link?
Abrahams from StdLib http://article.gmane.org/gmane.comp.lang.swift.user/1540:
Lawrence from Apple Developer Publications: https://twitter.com/_jackhl/status/646723367576276992
[…] Erica Sadun: […]
I’m just not sure why adding spaces in any case other than ternary even makes sense. It definitely looks bizare to me.
Naturally the ternary operator needs spaces, but I’m in favor of no spaces between a parameter name/label and its argument, eg:
let x = foo( param1:value1, param2:value2 )
My brains splits on whitespace, so this is the most easily scannable form for me.
Why then, put a space after the comma, or around the equals. Readability. Whenever I see things like this, I think lazy programmer. (Note, I’m not calling YOU lazy.) It reminds me of developers that do this: if(a&&b), which shows two things I see all the time. Why the heck would anyone not put a space after the if, around the && operator? It can only be laziness.
I think it mostly depends on how good one’s vision is. (Mine is bad.) To my eyes and brain, spaces are unambiguous separators, but colons look sufficiently like the flanking characters that they just don’t stand out enough.
I do put spaces around operators, including &&. I agree that it helps readability, because one needs to quickly spot the operators in any expression.
The choice to forego spaces around a colon in a parameter list is a matter of visual chunking. When I read a parameter list, I want my brain to chunk each argument label and parameter value pair as a visual unit. If I’m going to read the Nth argument label, or its corresponding value, I do that all at once; I never read one without the other. So, the important thing to me is being able to quickly parse the list and group together each label/value pair. The spaces between pairs help with that, whereas spaces around the colon make it harder.
As a quick demonstration in visual chunking, consider the following lines:
blerg( jfgkladfg:awercmx, dgagafd:awxsdlsd, sdfjlwds:asdfcx, adagfg:asdaf );
blerg( jfgkladfg: awercmx, dgagafd: awxsdlsd, sdfjlwds: asdfcx, adagfg: asdaf );
blerg( jfgkladfg : awercmx, dgagafd : awxsdlsd, sdfjlwds : asdfcx, adagfg : asdaf );
The top line has 4 visual groups in the argument list, which is exactly the number of things I need to read when skimming. My brain naturally gravitates towards reading the start of each argument label. The 2nd and 3rd lines have 8 visual gaps where my eyes stop to skim, which is not what I need. My eyes don’t ever need to stop on an argument value without first reading it’s parameter label.
Anyways, naturally this discussion over spaces is a trifle, and comes down to personal preference 🙂 Hopefully whatever style you’re using, it’s consistent within a particular project / team.