Dot-prefixing considered ugly

It is not immediately obvious without looking at the type itself:

  • what type is rawValue, is it a string or something else?
  • how many init(rawValue:) initialisers does this type have and what parameters do those accept? (Note that those initialisers could be in another files.)

self.init(rawValue: String(substring)) makes it crystal clear what particular init we are talking about without looking any further and without any extra knowledge; and BTW, there's normally no need to write String(substring) as String.init(substring).

i have to disagree, usually this appears in such close proximity to the enum definition itself that it really should be immediately obvious that in

@frozen public
enum ContentType:String, Equatable, Hashable, Sendable
{
    case html
    case css
    case javascript
}
extension ContentType:LosslessStringConvertible
{
    @inlinable public
    init?(_ description:some StringProtocol)
    {
        self.init(rawValue: .init(description))
    }
}

the rawValue is a String.

In some contexts there is actually a piece of information that is currently only communicated to the reader if you use the implicit initializer.

I was going to bring up this point earlier, but then I remembered about SE-0299 and I assumed that it was a moot point. Then I checked with the compiler (see below) and to my surprise, at least for now, it is a relevant point.

/// By looking at this function call
/// I know for sure (for now) that the
/// `downloadSettings:` argument has a concrete type.
downloadVideo(
    metadata: selectedVideo.metadata,
    downloadSettings: .init(continueInBackground: true)
)

/// That piece of information is lost here,
/// which may cause me to wonder if I can
/// define my own settings type to modify the behavior
downloadVideo(
    metadata: selectedVideo.metadata,
    downloadSettings: VideoDownloadSettings(continueInBackground: true)
)
(The snippet that demonstrates that `.init` cannot (yet) be used if the argument is generic.)
protocol MyProtocol { }
struct MyStruct: MyProtocol { }
func takesMyProtocol (_ value: some MyProtocol) { }
extension MyProtocol where Self == MyStruct {
    static func foo () -> Self { MyStruct() }
    init (foo: Void) { self.init() }
}
func demo () {
    takesMyProtocol(.foo()) // ✅ This works
    takesMyProtocol(.init(foo: ())) // ⛔️ This doesn't (yet)
}

related: Inconsistent SE-0299 behavior if closure parameters are unnamed?

@Karl, I've found that I actually think you're right about this in many situations. After considering your perspective and continuing to code I have indeed found places where I felt that readability was improved by replacing .init with the full type name. The leading dot + code completion is still how I start the expression (because it's like an order of magnitude faster and easier), but then I go back and fill in the type name, often. It would be cool for IDEs to make it easy to retroactively replace .init with the type name when desired.

There is still a common (for me) situation though where I find `.init` to be more readable than writing out the type name.

The reason it may only be common for me is because my preferred way of formatting function signatures plays an important role. Consider this function I was just working with:

///
public func createAdvertiser
    (discoveryInfo: [String : String]?,
     serviceType: String)
-> Advertiser {
    .init(
        localPeerID: localPeerID,
        serviceType: serviceType,
        discoveryInfo: discoveryInfo,
        physicalSpace: physicalSpace,
        physicalLocation: physicalLocation
    )
}

I prefer what's above to writing out the type name as seen here:

///
public func createAdvertiser
    (discoveryInfo: [String : String]?,
     serviceType: String)
-> Advertiser {
    Advertiser(
        localPeerID: localPeerID,
        serviceType: serviceType,
        discoveryInfo: discoveryInfo,
        physicalSpace: physicalSpace,
        physicalLocation: physicalLocation
    )
}

The thing is that sometimes I will opt for adding some more visual distinction between the signature and the implementation, in which case given the new opinion you've convinced me of I might go back to spelling out the type name:

///
public func createAdvertiser
    (discoveryInfo: [String : String]?,
     serviceType: String)
-> Advertiser {

    ///
    Advertiser(
        localPeerID: localPeerID,
        serviceType: serviceType,
        discoveryInfo: discoveryInfo,
        physicalSpace: physicalSpace,
        physicalLocation: physicalLocation
    )
}

I'll keep playing with it.

2 Likes

kind of off-topic, but how did you configure your IDE to do the five-space indent for the function parameters?

Hm, I didn’t do it explicitly and I just checked the Xcode settings and I don’t see anything about it. I have of course noticed and appreciated that the second, third, etc. parameters get that fifth space to line up properly with the first parameter.

This is not an exhaustive list, but every time I see "foo", "bar" (the most prevalent), or "considered", I feel a little physical disgust in my midsection, not dissimilar from when I swallow too big of a capybara. I understand that it comes naturally for some people, but I think it's unnecessary gatekeeping.

1 Like
Actually I discovered the answer ...

I have my way of doing it so physically internalized that I forgot that I leverage idiosyncratic aspects of Xcode's default editing behavior to make the parameters line up.

Specifically, the idiosyncrasy to leverage is the following. If you are here:

public func createAdvertiser
    (discoveryInfo: [String : String]?,| (👈 that's the cursor) 

then if you hit enter the cursor will line up wrong (i.e., when you start typing it will line up with the parenthesis, not with the first letter of the first parameter):

public func createAdvertiser
    (discoveryInfo: [String : String]?,
    ser

but if you first type a single letter, then hit the left arrow, and then hit enter, you will find that the letter you have typed is lined up with the first letter of parameter above, not with the parenthesis, and you may merrily jump to the end of the line and keep typing:

public func createAdvertiser
    (discoveryInfo: [String : String]?,
     serviceType: String)
4 Likes