Inconsistencies related to prepositions

When Swift 3 launched, it introduced a new concept of placing the preposition inside the parentheses. (See discussion here: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009523.html).

I'm fine with that, however this change got implemented in an inconsistent manner, making Swift and its APIs more vague, thereby decreasing code clarity.

I was hoping to see these inconsistencies get cleared up with Swift 4, however they were not. I realized that perhaps I could have spoken up and taken more of an active role in Swift Evolution. So... better late than never. Here are my thoughts, towards consistency and disambiguation in Swift.

Disclaimer: I’m sure the last thing anyone wants are more changes to the APIs. However, I also don't think these inconsistencies should be left in place forever. I have tried to go back and read through as much of the relevant discussions on list as I could, and it seems like

Please let me some give examples below. After that is some discussion and a proposal.

EXAMPLES

Take for example the preposition "with." A preposition is meaningless without a subject and an object. Look at the following three sentences:

(A) "A boy with the dog Molly walked across the street."

(B) "With the dog Molly walked across the street."

(C) "A boy with Molly walked across the street."

Sentence (A) is longest, but it makes perfect sense.

Sentence (B) is nonsensical and grammatically incorrect because while the word "with" takes two arguments, only one is present. If there was a comma after "dog," then it would make sense; "With the dog, Molly walked across the street" is fine. But if we were to assume that Molly is not the dog, in this case, we'd actually be wrong.

Sentence (C), while grammatically correct, leaves it unclear whether Molly is a dog, a girl, or… something else.

The reason for this is, whenever a preposition is used in English, it almost always takes a dyadic form, relating a subject to the preposition's object. The two most common dyadic formats are:

<subject> [<preposition> <object of preposition>]
<The boy> [<with> <the dog>] crossed the street.

[<preposition> < object of preposition>] <subject>
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature were generally respected in method signatures. However, Swift 3's migration of the preposition inside the parentheses also seems to have been accompanied by the stripping away of either the subject, the prepositional object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
(subject "Record" got removed)

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
(subject "String" and prep. object "Locale" both got removed)

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
(subject "element" already missing from both; prep. object "Index" got removed)

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
(subject "timeInterval" and object "Date" both still present; but oddly, preposition "since" got left outside of the parentheses)

(5) Array:

      old: myArray.forEach({ thing in code})
new: myArray.forEach() { thing in //code }
            (preposition “for” is outside of the parentheses)

DISCUSSION OF EXAMPLES

These four changes are inconsistent with each other regarding whether the subject, prepositional object, or both were removed, and we don't even have consistency as to whether the preposition got moved inside the parentheses (see A below).

As well, these changes generally removed the dyadic arguments to the preposition, which removes cues necessary to disambiguate the prepositional relationship, decreasing code readability (see B below).

(A) Inconsistency

The inconsistency between the examples is shown in the bold text of each example, but lets go over why this matters. It matters because any language is easier to learn the more consistently it sticks to its own rules. Autocomplete is our great savior, but still, if we were being consistent, then the new method signatures would have been:

(1) myCKDatabase.fetchRecord(withRecordID:)
(2) myString.stringCapitalized(withLocale:)
(3) myDictionary.elementRemoved(atIndex:)
(4) myDate.timeInterval(sinceDate:)
(5) myArray.each(inClosure: )

Side note: for plain English readability, we might prefer elementRemoved(fromIndex:) and stringCapitlized(accordingToLocale:).

Although I do understand removing "string" from the latter was to reduce redundancy in function/method declarations, we only make one declaration, yet we make many calls. So increasing ambiguity in calls does not seem like a good trade-off for decreased boilerplate in declarations. More often than not it's calls that we're reading, not the declarations—unless of course the call was ambiguous and we had to read the declaration to make sense out of it. So perhaps we might question if increased ambiguity is an overall good thing.

Side note: example (5), .forEach, does seem like a very exceptional case, and possibly a sacred cow. See the Proposal section for further discussion of this.

(B) Increased Ambiguity

In all of these changes, one of or both parts of the dyadic arguments of the preposition have been excised from the method signatures. This increases ambiguity in Swift 3/4 vs. Objective C and Swift 2, especially in closures, as I will explain below.

In example (1), the old method argument makes grammatical sense because "record with RecordID" follows the format, <subject> [<preposition> <object of preposition>], just like "boy with dog tag" or "cow with black spots." This improves code readability, because when you read this, you know that this function will give you a record matching a particular recordID. However in Swift 3, we have the equivalent of, "with a recordID"—i.e. is *implied* that the thing being fetched is a record.

This isn't a problem you're reading the method signature in the header, and it's not a problem when you're writing the code, because you'll get warnings and errors if you try to assign to the wrong type.

However this removal of explicit contextual cues from the method signature harms readability, since now, the compiler will let people write code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short variable names or NO variable names. However, spend much time on GitHub or in CocoaPods and you will see an increasing number of codebases where that's exactly what they do, especially in closures.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no reason to expect the type of "myRecordID" not to be CKRecordID. (In fact, it's CKRecord.) There is also no way to clarify what ambiguousName is since closures don't have argument labels at all... but that's another problem altogether.

Turning now to example (2), "myString.capitalized(with:myLocale)" sacrifices BOTH the subject and the prepositional object. We have now lost any enforced contextual cues, fully orphaning "with", allowing and even encouraging a final closure argument like:

{ return $0.capitalized(with: .current)) }

What is $0? Reading this, and being familiar with Swift, you will likely assume it's a string, but will you remember that .current is a Locale? The compiler knows, but why was it a good idea to strip out that information?

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

We could also have:

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value pair, array will be a dict, and key will be an index type! This may seem like a ridiculous example, but I have literally seen things like this.

DISCUSSION

Making computer code more like natural language and thus more approachable to non-computer-science types was clearly one of the primary goals of Apple's method names in Objective C.

I've been a fan of the general direction in which Swift has taken things, but I'm starting to see code that is quite unreadable as a result of the stripping away of useful information from code, as if more white space will improve our ability to make sense out of our arcane invocations.

The point of code readability is for humans to be able to read and understand code with minimal extra effort. Explicit information is important because humans would like to read code without having to dig through five different headers and read lots of documentation files. This directly increases costs, because when a new developer comes onto a project, the longer it takes them to understand the codebase, the more it will cost the company.

I don't believe the intent with these changes was to increase ambiguity; at WWDC '16 we were told it was to increase clarity. I'm just not sure that's the consistent effect we actually received.

What we got instead is more times we will need to CMD-click things in XCode to get a clue. That's especially annoying because you're at XCode's mercy as to whether it actually wants to jump to a given declaration or not. This seems pretty hit or miss in my experience, even within a basic playground page, and is especially troublesome with jumping to declarations within CocoaPods. Selecting some code to get documentation in the side panel seems equally unreliable.

Perhaps CMD-click problems have to do with a project setting, but is there really a time when you would ever NOT want to have these capabilities? Why is it tied into a setting? Further, when you're reading through a repo on GitHub itself, you don't have CMD-click capability; you want the code to be as clear as possible, without lots of ambiguity.

From Ambiguous to Cryptic

Orphaning method signatures by stripping useful return type and argument type information wouldn't be so bad if variables were all named descriptively, but that is a strangely optimistic hope for a language that's as paranoid about safety that it was specifically designed to prevent many categories of common mistakes.

The increased ambiguity in Swift from the removal of prepositional components has been further compounded by other forms of ambiguity that are increasingly everywhere in Swift, and which developers have taken copious advantage of to the point of abuse. Examples are:

- arguments labelled as "_" reduce code clarity
- calls to functions that take closures as the final argument can leave the final argument label out, decreasing readability
- closures have lack explicit type information, leaving it up to variable names to let us know what's going on; however, the use $# syntax and/or cryptically named variables often leaves it totally ambiguous
- generic types in method signatures using a single letter give no clue as to the type's purpose
- function types can't have argument labels, which might give a sense of context or informative cues, even if they added boilerplate
- inferred nested type parents (like the ability to do .current instead of being forced to do Location.current)

Why is removing context and clue good?

Is it possible that this was an overreaction to the perceived over-verbosity of Objective C? I'm honestly curious. I will try to read back through the archives, but perhaps someone can give me a good summary.

In the old Objective C style, method names could be quite long, but I never felt a lack of context that made code unclear. The method signatures were basically like sentences without spaces, and they seemed to follow pretty reliable rules. So reading them actually made grammatical sense, improving code readability. I could always tell what a call was doing.

While I won't deny that Objective C went overboard sometimes in verbosity, and while few would dispute that we should clean up its style, could it be we are going too far making Swift encourage code that's ambiguous to everyone except the compiler? Could it be a mistake for Swift to err so far on the side of inferring everything and stripping away contextual information?

A first-time reader of some unseen code is not going to be able to infer things like the original programmer or the compiler can. This increases the possibility of bugs (and exploits) sneaking in "under the radar."

PROPOSAL

To add more clarity back to Swift, I'd propose the following rules be applied consistently across the language:

• Every time a preposition shows up in a function/method signature, there is an explicit subject and object of the preposition, except in the following exceptions.

• The subject of a preposition can be left out if the function/method is mutating AND doesn't return a value, such as Array's .append(contentsOf:) method. This method would become: .append(contentsOfCollection:) so that it's clear what we're doing here, even within a closure.

• No preposition should be used when it would add no extra meaning between what’s inside and outside the parentheses. For example, userFont(ofSize fontSize:) should just be userFont(size: fontSize), because “of” is completely superfluous here. AVFragmentedMovie’s track(withTrackID:) could just as easily be track(forTrackID:) or track(TrackID:) which tells us that the preposition is superfluous and so why not just have it be track(id:).

• If the preposition goes with a verb in the method signature, the verb should be the last thing before the opening parenthesis (e.g. String's .capitalized function would now look like name.stringCapitalized(accordingToLocale: .current)... no pun intended).

• In the case of .forEach, since it’s almost identical in function to .map, perhaps a solution could be to eliminate .forEach entirely, and simply say that anytime .map’s result is unused (or assigned to _), then the compiler will automatically use .forEach functionality to optimize performance. For example if in Swift 5 you did _ = myArray.map() { print($0) }, this would be the same as doing myArray.forEach() { print($0) } currently. As well, _ = could be inferred.

Note: part of the problem is obviously that parameter names are never used in function calls when there is an argument label, but often, the argument label is now just a preposition, while its prepositional object is the (now hidden) parameter name. Perhaps we can think of a novel solution to this?

I'm interested to know what people think about this. Perhaps there are other ideas or ways to add clarity back in. Thanks for reading this long message.

Jon

I agree with some of your points and disagree with others. You are right that Swift did not get everything right when it “Swiftified” some Objective-C names. However, I don’t think your criticisms are all correct.

At some point, Swift decided to omit needless “helpful” names from method names. This is a good thing. You don’t “string-capitalize” a string; you capitalize it. And since types are known at compile time, there isn’t really a need to name the type of every argument. This would hurt the readability/Englishness of Swift. For instance, `append(contentsOf:)` — in English, you would say “append the contents of that bucket to my array”, not “append the contents of collection that bucket to my array”. Only collections can have contents so you can omit that type, both in English and in Swift. In short, when the argument label clearly implies the type of the argument, there’s no need to explicitly state it. However when that type is not clear then it should be specified.

I think Swift did move in a weird direction with method base-names, as shown in some of your examples. It relies too heavily on a shared verb among methods with argument labels serving as the primary differentiators, when sometimes part of the label should be attached to the verb. And for arguments, it relies too heavily on type information to understand the functionality; somewhat-hidden type information (inspectable in Xcode, but not very obvious if you’re just reading quickly) should be made visible in the argument labels where necessary.

To your specific examples:

`fetch(withRecordID)` leaves unanswered the question “what is being fetched?”, instead forcing you to infer the answer from the question “how is the thing being fetched, being fetched?” — “it is being fetched by record ID so I guess it must be a record”. Is there even such a thing as a record ID? I think not — there are just IDs, some of which are tied to records. I think that that method should be called `fetchRecord(withID:)`. This makes it clear that a) a record is being fetched and b) its ID is what is used to fetch it.
`string.capitalized(with:)` it very unclear. As an uneducated programmer, I have no idea what you capitalize a string “with”. With a font? With some Unicode machinery? With gusto? (Ok that last one was a joke.) It’s not at all obvious that you capitalize a string with a locale, and hence I think that that method should be called `string.capitalized(withLocale:)`. I explain above why it should be string.capitalized and not string.stringCapitalized.
`dict.remove(at:)` is a bit of a tougher case. It is mutating, so it has to be in the present tense. Yet it does return an element, so maybe it should state that? I think the deciding vote is that it’s @discardableResult, which means it should be treated as primarily removing the element; returning it is secondary. Maybe to indicate the returning of the element, it should be called `dict.pop(at:)`. “Pop" implies the element popped, and probably implies @discardableResult in most programmers’ minds as well.
`date.timeInterval` is another tricky one. IMO the most natural choice is `date.timeIntervalSince(_:)`. I don’t think the date argument label is necessary because given a date, you can only compute a time interval since… another date. See the `array.append(contentsOf:)` example above. And although `since` is a preposition, it should be part of the method name, and not an argument label, because dates do not just “have” time intervals. A database can fetch a record, but a date does not have a time interval — it has a time interval since (another date).
This should definitely be `forEach`. `array.each` would be another name for `array.everyElementSatisfiesCondition(_ f: (Element)->Bool))`. “For each” specifies that you will do something *for each* element as opposed to asking a question about each element. In English, “for each thing in my bucket…” will probably be followed by “do this thing”. You could say “for each thing in my bucket, it’s true that…” but you’d probably just say “everything in my bucket is…"

So, if I had to come up with my own rules for naming conventions:

Methods should first state what they do, then how they do it. You fetch a record using an ID; you don’t fetch a thing using a record ID. Ask the question “does the calling variable have such-and-such a thing” or “can the calling variable do such-and-such a thing”, and be specific about exactly what the thing is. If yes, than that thing is a good candidate for a method name. A database can fetch, sure, but more specifically it can fetch records, and that’s about as specific as you can get. A date doesn’t have a collection of time intervals sitting around, but it can compute the *time interval since* another date. Strings can be capitalized (with a locale), dictionaries can remove (at an index), arrays can append (the contents of some collection).
When the preposition/argument label makes it clear what sort of argument it takes, then it’s fine to omit the type. However if the argument label is vague, then the argument type should be included. In this case the type is not just a type specifier — it’s really part of the English description. You don’t say “append the contents of collection that bucket to my array”, but any sentence about capitalizing a string with a locale would use the word “locale” explicitly, either in the name of the variable or otherwise — “capitalize that string with your specified locale”, “capitalize that string with locale XYZ”, etc. The English language doesn’t really have the notion of implicitly locale-like objects the way it has implicitly collection-like objects such as buckets, bags, etc, so the fact that a locale is being used should be explicit while the fact that a collection is being used may be implicit.
As an aside, I feel that “using Locale” would be better than “with Locale”, but that’s a conversation for another time.
Three nouns may be required in a name, as in the fetch record example. The calling variable is always the subject, and there may be either one or two objects in play. If there are two, then one should go in the method name; the other may or may not be made explicit in the argument label, depending on clarity (see the above point). When a database fetches a record using its ID, you have only two choices: db.fetchRecord(with:) or db.fetchRecord(withID:). In this case, clarity would lead to the second choice, as you don’t know what you’re fetching the record *with*. In the date example, the calling date is the subject, and it is *producing a time interval since* another date. So again, two choices: date.timeIntervalSince(date:) or date.timeIntervalSince(_:). Since you can only find the time interval between two dates, `date:` can be omitted.

It’s hard to make hard-and-fast rules around this because language is far from hard-and-fast itself. But I think these would be good guidelines.

···

On Aug 2, 2017, at 11:44 AM, Jon Gilbert via swift-evolution <swift-evolution@swift.org> wrote:

When Swift 3 launched, it introduced a new concept of placing the preposition inside the parentheses. (See discussion here: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009523.html).

I'm fine with that, however this change got implemented in an inconsistent manner, making Swift and its APIs more vague, thereby decreasing code clarity.

I was hoping to see these inconsistencies get cleared up with Swift 4, however they were not. I realized that perhaps I could have spoken up and taken more of an active role in Swift Evolution. So... better late than never. Here are my thoughts, towards consistency and disambiguation in Swift.

Disclaimer: I’m sure the last thing anyone wants are more changes to the APIs. However, I also don't think these inconsistencies should be left in place forever. I have tried to go back and read through as much of the relevant discussions on list as I could, and it seems like

Please let me some give examples below. After that is some discussion and a proposal.

EXAMPLES

Take for example the preposition "with." A preposition is meaningless without a subject and an object. Look at the following three sentences:

(A) "A boy with the dog Molly walked across the street."

(B) "With the dog Molly walked across the street."

(C) "A boy with Molly walked across the street."

Sentence (A) is longest, but it makes perfect sense.

Sentence (B) is nonsensical and grammatically incorrect because while the word "with" takes two arguments, only one is present. If there was a comma after "dog," then it would make sense; "With the dog, Molly walked across the street" is fine. But if we were to assume that Molly is not the dog, in this case, we'd actually be wrong.

Sentence (C), while grammatically correct, leaves it unclear whether Molly is a dog, a girl, or… something else.

The reason for this is, whenever a preposition is used in English, it almost always takes a dyadic form, relating a subject to the preposition's object. The two most common dyadic formats are:

<subject> [<preposition> <object of preposition>]
<The boy> [<with> <the dog>] crossed the street.

[<preposition> < object of preposition>] <subject>
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature were generally respected in method signatures. However, Swift 3's migration of the preposition inside the parentheses also seems to have been accompanied by the stripping away of either the subject, the prepositional object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
(subject "Record" got removed)

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
(subject "String" and prep. object "Locale" both got removed)

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
(subject "element" already missing from both; prep. object "Index" got removed)

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
(subject "timeInterval" and object "Date" both still present; but oddly, preposition "since" got left outside of the parentheses)

(5) Array:

      old: myArray.forEach({ thing in code})
new: myArray.forEach() { thing in //code }
            (preposition “for” is outside of the parentheses)

DISCUSSION OF EXAMPLES

These four changes are inconsistent with each other regarding whether the subject, prepositional object, or both were removed, and we don't even have consistency as to whether the preposition got moved inside the parentheses (see A below).

As well, these changes generally removed the dyadic arguments to the preposition, which removes cues necessary to disambiguate the prepositional relationship, decreasing code readability (see B below).

(A) Inconsistency

The inconsistency between the examples is shown in the bold text of each example, but lets go over why this matters. It matters because any language is easier to learn the more consistently it sticks to its own rules. Autocomplete is our great savior, but still, if we were being consistent, then the new method signatures would have been:

(1) myCKDatabase.fetchRecord(withRecordID:)
(2) myString.stringCapitalized(withLocale:)
(3) myDictionary.elementRemoved(atIndex:)
(4) myDate.timeInterval(sinceDate:)
(5) myArray.each(inClosure: )

Side note: for plain English readability, we might prefer elementRemoved(fromIndex:) and stringCapitlized(accordingToLocale:).

Although I do understand removing "string" from the latter was to reduce redundancy in function/method declarations, we only make one declaration, yet we make many calls. So increasing ambiguity in calls does not seem like a good trade-off for decreased boilerplate in declarations. More often than not it's calls that we're reading, not the declarations—unless of course the call was ambiguous and we had to read the declaration to make sense out of it. So perhaps we might question if increased ambiguity is an overall good thing.

Side note: example (5), .forEach, does seem like a very exceptional case, and possibly a sacred cow. See the Proposal section for further discussion of this.

(B) Increased Ambiguity

In all of these changes, one of or both parts of the dyadic arguments of the preposition have been excised from the method signatures. This increases ambiguity in Swift 3/4 vs. Objective C and Swift 2, especially in closures, as I will explain below.

In example (1), the old method argument makes grammatical sense because "record with RecordID" follows the format, <subject> [<preposition> <object of preposition>], just like "boy with dog tag" or "cow with black spots." This improves code readability, because when you read this, you know that this function will give you a record matching a particular recordID. However in Swift 3, we have the equivalent of, "with a recordID"—i.e. is *implied* that the thing being fetched is a record.

This isn't a problem you're reading the method signature in the header, and it's not a problem when you're writing the code, because you'll get warnings and errors if you try to assign to the wrong type.

However this removal of explicit contextual cues from the method signature harms readability, since now, the compiler will let people write code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short variable names or NO variable names. However, spend much time on GitHub or in CocoaPods and you will see an increasing number of codebases where that's exactly what they do, especially in closures.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no reason to expect the type of "myRecordID" not to be CKRecordID. (In fact, it's CKRecord.) There is also no way to clarify what ambiguousName is since closures don't have argument labels at all... but that's another problem altogether.

Turning now to example (2), "myString.capitalized(with:myLocale)" sacrifices BOTH the subject and the prepositional object. We have now lost any enforced contextual cues, fully orphaning "with", allowing and even encouraging a final closure argument like:

{ return $0.capitalized(with: .current)) }

What is $0? Reading this, and being familiar with Swift, you will likely assume it's a string, but will you remember that .current is a Locale? The compiler knows, but why was it a good idea to strip out that information?

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

We could also have:

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value pair, array will be a dict, and key will be an index type! This may seem like a ridiculous example, but I have literally seen things like this.

DISCUSSION

Making computer code more like natural language and thus more approachable to non-computer-science types was clearly one of the primary goals of Apple's method names in Objective C.

I've been a fan of the general direction in which Swift has taken things, but I'm starting to see code that is quite unreadable as a result of the stripping away of useful information from code, as if more white space will improve our ability to make sense out of our arcane invocations.

The point of code readability is for humans to be able to read and understand code with minimal extra effort. Explicit information is important because humans would like to read code without having to dig through five different headers and read lots of documentation files. This directly increases costs, because when a new developer comes onto a project, the longer it takes them to understand the codebase, the more it will cost the company.

I don't believe the intent with these changes was to increase ambiguity; at WWDC '16 we were told it was to increase clarity. I'm just not sure that's the consistent effect we actually received.

What we got instead is more times we will need to CMD-click things in XCode to get a clue. That's especially annoying because you're at XCode's mercy as to whether it actually wants to jump to a given declaration or not. This seems pretty hit or miss in my experience, even within a basic playground page, and is especially troublesome with jumping to declarations within CocoaPods. Selecting some code to get documentation in the side panel seems equally unreliable.

Perhaps CMD-click problems have to do with a project setting, but is there really a time when you would ever NOT want to have these capabilities? Why is it tied into a setting? Further, when you're reading through a repo on GitHub itself, you don't have CMD-click capability; you want the code to be as clear as possible, without lots of ambiguity.

From Ambiguous to Cryptic

Orphaning method signatures by stripping useful return type and argument type information wouldn't be so bad if variables were all named descriptively, but that is a strangely optimistic hope for a language that's as paranoid about safety that it was specifically designed to prevent many categories of common mistakes.

The increased ambiguity in Swift from the removal of prepositional components has been further compounded by other forms of ambiguity that are increasingly everywhere in Swift, and which developers have taken copious advantage of to the point of abuse. Examples are:

- arguments labelled as "_" reduce code clarity
- calls to functions that take closures as the final argument can leave the final argument label out, decreasing readability
- closures have lack explicit type information, leaving it up to variable names to let us know what's going on; however, the use $# syntax and/or cryptically named variables often leaves it totally ambiguous
- generic types in method signatures using a single letter give no clue as to the type's purpose
- function types can't have argument labels, which might give a sense of context or informative cues, even if they added boilerplate
- inferred nested type parents (like the ability to do .current instead of being forced to do Location.current)

Why is removing context and clue good?

Is it possible that this was an overreaction to the perceived over-verbosity of Objective C? I'm honestly curious. I will try to read back through the archives, but perhaps someone can give me a good summary.

In the old Objective C style, method names could be quite long, but I never felt a lack of context that made code unclear. The method signatures were basically like sentences without spaces, and they seemed to follow pretty reliable rules. So reading them actually made grammatical sense, improving code readability. I could always tell what a call was doing.

While I won't deny that Objective C went overboard sometimes in verbosity, and while few would dispute that we should clean up its style, could it be we are going too far making Swift encourage code that's ambiguous to everyone except the compiler? Could it be a mistake for Swift to err so far on the side of inferring everything and stripping away contextual information?

A first-time reader of some unseen code is not going to be able to infer things like the original programmer or the compiler can. This increases the possibility of bugs (and exploits) sneaking in "under the radar."

PROPOSAL

To add more clarity back to Swift, I'd propose the following rules be applied consistently across the language:

• Every time a preposition shows up in a function/method signature, there is an explicit subject and object of the preposition, except in the following exceptions.

• The subject of a preposition can be left out if the function/method is mutating AND doesn't return a value, such as Array's .append(contentsOf:) method. This method would become: .append(contentsOfCollection:) so that it's clear what we're doing here, even within a closure.

• No preposition should be used when it would add no extra meaning between what’s inside and outside the parentheses. For example, userFont(ofSize fontSize:) should just be userFont(size: fontSize), because “of” is completely superfluous here. AVFragmentedMovie’s track(withTrackID:) could just as easily be track(forTrackID:) or track(TrackID:) which tells us that the preposition is superfluous and so why not just have it be track(id:).

• If the preposition goes with a verb in the method signature, the verb should be the last thing before the opening parenthesis (e.g. String's .capitalized function would now look like name.stringCapitalized(accordingToLocale: .current)... no pun intended).

• In the case of .forEach, since it’s almost identical in function to .map, perhaps a solution could be to eliminate .forEach entirely, and simply say that anytime .map’s result is unused (or assigned to _), then the compiler will automatically use .forEach functionality to optimize performance. For example if in Swift 5 you did _ = myArray.map() { print($0) }, this would be the same as doing myArray.forEach() { print($0) } currently. As well, _ = could be inferred.

Note: part of the problem is obviously that parameter names are never used in function calls when there is an argument label, but often, the argument label is now just a preposition, while its prepositional object is the (now hidden) parameter name. Perhaps we can think of a novel solution to this?

I'm interested to know what people think about this. Perhaps there are other ideas or ways to add clarity back in. Thanks for reading this long message.

Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I agree with some of your points and disagree with others. You are right
that Swift did not get everything right when it “Swiftified” some
Objective-C names. However, I don’t think your criticisms are all correct.

At some point, Swift decided to omit needless “helpful” names from method
names. This is a good thing. You don’t “string-capitalize” a string; you
capitalize it. And since types are known at compile time, there isn’t
really a need to name the type of every argument. This would hurt the
readability/Englishness of Swift. For instance, `append(contentsOf:)` — in
English, you would say “append the contents of that bucket to my array”,
not “append the contents of collection that bucket to my array”. Only
collections can have contents so you can omit that type, both in English
and in Swift. In short, when the argument label clearly implies the type of
the argument, there’s no need to explicitly state it. However when that
type is not clear then it should be specified.

I think Swift did move in a weird direction with method base-names, as
shown in some of your examples. It relies too heavily on a shared verb
among methods with argument labels serving as the primary differentiators,
when sometimes part of the label should be attached to the verb. And for
arguments, it relies too heavily on type information to understand the
functionality; somewhat-hidden type information (inspectable in Xcode, but
not very obvious if you’re just reading quickly) should be made visible in
the argument labels where necessary.

To your specific examples:

   1. `fetch(withRecordID)` leaves unanswered the question “what is being
   fetched?”, instead forcing you to infer the answer from the question “how
   is the thing being fetched, being fetched?” — “it is being fetched by
   record ID so I guess it must be a record”. Is there even such a thing as a
   record ID? I think not — there are just IDs, some of which are tied to
   records. I think that that method should be called `fetchRecord(withID:)`.
   This makes it clear that a) a record is being fetched and b) its ID is what
   is used to fetch it.
   2. `string.capitalized(with:)` it very unclear. As an uneducated
   programmer, I have no idea what you capitalize a string “with”. With a
   font? With some Unicode machinery? With gusto? (Ok that last one was a
   joke.) It’s not at all obvious that you capitalize a string with a locale,
   and hence I think that that method should be called
   `string.capitalized(withLocale:)`. I explain above why it should be
   string.capitalized and not string.stringCapitalized.

This topic was reviewed extensively at the beginning of Swift Evolution.
The idea is that type information does not need to be repeated in the
label. Since the locale is of type Locale, “with” is preferred over
“withLocale” because “withLocale: Locale” is redundant. As this has been
long approved by the community, it’s not in scope to roll it back.

   1.
   2. `dict.remove(at:)` is a bit of a tougher case. It is mutating, so
   it has to be in the present tense. Yet it does return an element, so maybe
   it should state that? I think the deciding vote is that it’s
   @discardableResult, which means it should be treated as primarily removing
   the element; returning it is secondary. Maybe to indicate the returning of
   the element, it should be called `dict.pop(at:)`. “Pop" implies the element
   popped, and probably implies @discardableResult in most programmers’ minds
   as well.

Brent had a draft proposal to revise the names of collection methods to
improve the situation here. There is room for improvement.

···

On Wed, Aug 2, 2017 at 12:00 Robert Bennett via swift-evolution < swift-evolution@swift.org> wrote:

   1.
   2. `date.timeInterval` is another tricky one. IMO the most natural
   choice is `date.timeIntervalSince(_:)`. I don’t think the date argument
   label is necessary because given a date, you can only compute a time
   interval since… another date. See the `array.append(contentsOf:)` example
   above. And although `since` is a preposition, it should be part of the
   method name, and not an argument label, because dates do not just “have”
   time intervals. A database can fetch a record, but a date does not have a
   time interval — it has a time interval since (another date).
   3. This should definitely be `forEach`. `array.each` would be another
   name for `array.everyElementSatisfiesCondition(_ f: (Element)->Bool))`.
   “For each” specifies that you will do something *for each* element as
   opposed to asking a question about each element. In English, “for each
   thing in my bucket…” will probably be followed by “do this thing”. You
   could say “for each thing in my bucket, it’s true that…” but you’d probably
   just say “everything in my bucket is…"

So, if I had to come up with my own rules for naming conventions:

   1. Methods should first state what they do, then how they do it. You
   fetch a record using an ID; you don’t fetch a thing using a record ID. Ask
   the question “does the calling variable have such-and-such a thing” or “can
   the calling variable do such-and-such a thing”, and be specific about
   exactly what the thing is. If yes, than that thing is a good candidate for
   a method name. A database can fetch, sure, but more specifically it can
   fetch records, and that’s about as specific as you can get. A date doesn’t
   have a collection of time intervals sitting around, but it can compute the
   *time interval since* another date. Strings can be capitalized (with a
   locale), dictionaries can remove (at an index), arrays can append (the
   contents of some collection).
   2. When the preposition/argument label makes it clear what sort of
   argument it takes, then it’s fine to omit the type. However if the argument
   label is vague, then the argument type should be included. In this case the
   type is not just a type specifier — it’s really part of the English
   description. You don’t say “append the contents of collection that bucket
   to my array”, but any sentence about capitalizing a string with a locale
   would use the word “locale” explicitly, either in the name of the variable
   or otherwise — “capitalize that string with your specified locale”,
   “capitalize that string with locale XYZ”, etc. The English language doesn’t
   really have the notion of implicitly locale-like objects the way it has
   implicitly collection-like objects such as buckets, bags, etc, so the fact
   that a locale is being used should be explicit while the fact that a
   collection is being used may be implicit.
   As an aside, I feel that “using Locale” would be better than “with
   Locale”, but that’s a conversation for another time.
   3. Three nouns may be required in a name, as in the fetch record
   example. The calling variable is always the subject, and there may be
   either one or two objects in play. If there are two, then one should go in
   the method name; the other may or may not be made explicit in the argument
   label, depending on clarity (see the above point). When a database fetches
   a record using its ID, you have only two choices: db.fetchRecord(with:) or
   db.fetchRecord(withID:). In this case, clarity would lead to the second
   choice, as you don’t know what you’re fetching the record *with*. In the
   date example, the calling date is the subject, and it is *producing a time
   interval since* another date. So again, two choices:
   date.timeIntervalSince(date:) or date.timeIntervalSince(_:). Since you can
   only find the time interval between two dates, `date:` can be omitted.

It’s hard to make hard-and-fast rules around this because language is far
from hard-and-fast itself. But I think these would be good guidelines.

On Aug 2, 2017, at 11:44 AM, Jon Gilbert via swift-evolution < > swift-evolution@swift.org> wrote:

When Swift 3 launched, it introduced a new concept of placing the
preposition inside the parentheses. (See discussion here:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009523.html
).

I'm fine with that, however this change got implemented in an inconsistent
manner, making Swift and its APIs more vague, thereby decreasing code
clarity.

I was hoping to see these inconsistencies get cleared up with Swift 4,
however they were not. I realized that perhaps I could have spoken up and
taken more of an active role in Swift Evolution. So... better late than
never. Here are my thoughts, towards consistency and disambiguation in
Swift.

Disclaimer: I’m sure the last thing anyone wants are more changes to the
APIs. However, I also don't think these inconsistencies should be left in
place forever. I have tried to go back and read through as much of the
relevant discussions on list as I could, and it seems like

Please let me some give examples below. After that is some discussion and
a proposal.

*EXAMPLES*

Take for example the preposition "with." A preposition is meaningless
without a subject and an object. Look at the following three sentences:

(A) "A boy with the dog Molly walked across the street."

(B) "With the dog Molly walked across the street."

(C) "A boy with Molly walked across the street."

Sentence (A) is longest, but it makes perfect sense.

Sentence (B) is nonsensical and grammatically incorrect because while the
word "with" takes two arguments, only one is present. If there was a comma
after "dog," then it would make sense; "With the dog, Molly walked across
the street" is fine. But if we were to assume that Molly is not the dog, in
this case, we'd actually be wrong.

Sentence (C), while grammatically correct, leaves it unclear whether Molly
is a dog, a girl, or… something else.

The reason for this is, whenever a preposition is used in English, it
almost always takes a dyadic form, relating a subject to the preposition's
object. The two most common dyadic formats are:

*<subject> [<preposition> <object of preposition>]*
<The boy> [<with> <the dog>] crossed the street.

*[<preposition> <** object of preposition**>] <subject>*
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature
were generally respected in method signatures. However, Swift 3's migration
of the preposition inside the parentheses also seems to have been
accompanied by the stripping away of either the subject, the prepositional
object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
*(subject "Record" got removed)*

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
*(subject "String" and prep. object "Locale" both got removed)*

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
*(subject "element" already missing from both; prep. object "Index" got
removed)*

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
*(subject "timeInterval" and object "Date" both still present; but oddly,
preposition "since" got left outside of the parentheses)*

(5) Array:

    old: myArray.forEach({ thing in code})

new: myArray.forEach() { thing in //code }

            *(preposition “for” is outside of the parentheses)*

*DISCUSSION OF EXAMPLES*

These four changes are inconsistent with each other regarding whether the
subject, prepositional object, or both were removed, and we don't even have
consistency as to whether the preposition got moved inside the parentheses
(see A below).

As well, these changes generally removed the dyadic arguments to the
preposition, which removes cues necessary to disambiguate the prepositional
relationship, decreasing code readability (see B below).

*(A) Inconsistency*

The inconsistency between the examples is shown in the bold text of each
example, but lets go over why this matters. It matters because any language
is easier to learn the more consistently it sticks to its own rules.
Autocomplete is our great savior, but still, if we were being consistent,
then the new method signatures would have been:

(1) myCKDatabase.fetchRecord(withRecordID:)
(2) myString.stringCapitalized(withLocale:)
(3) myDictionary.elementRemoved(atIndex:)
(4) myDate.timeInterval(sinceDate:)
(5) myArray.each(inClosure: )

Side note: for plain English readability, we might prefer
elementRemoved(fromIndex:) and stringCapitlized(accordingToLocale:).

Although I do understand removing "string" from the latter was to reduce
redundancy in function/method declarations, we only make one declaration,
yet we make many calls. So increasing ambiguity in calls does not seem like
a good trade-off for decreased boilerplate in declarations. More often than
not it's calls that we're reading, not the declarations—unless of course
the call was ambiguous and we had to read the declaration to make sense out
of it. So perhaps we might question if increased ambiguity is an overall
good thing.

Side note: example (5), .forEach, does seem like a very exceptional case,
and possibly a sacred cow. See the Proposal section for further discussion
of this.

*(B) Increased Ambiguity*

In all of these changes, one of or both parts of the dyadic arguments of
the preposition have been excised from the method signatures. This
increases ambiguity in Swift 3/4 vs. Objective C and Swift 2, especially in
closures, as I will explain below.

In example (1), the old method argument makes grammatical sense because
"record with RecordID" follows the format, *<subject> [<preposition>
<object of preposition>]*, just like "boy with dog tag" or "cow with
black spots." This improves code readability, because when you read this,
you know that this function will give you a record matching a particular
recordID. However in Swift 3, we have the equivalent of, "with a
recordID"—i.e. is *implied* that the thing being fetched is a record.

This isn't a problem you're reading the method signature in the header,
and it's not a problem when you're writing the code, because you'll get
warnings and errors if you try to assign to the wrong type.

However this removal of explicit contextual cues from the method signature
harms readability, since now, the compiler will let people write code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short
variable names or NO variable names. However, spend much time on GitHub or
in CocoaPods and you will see an increasing number of codebases where
that's exactly what they do, especially in closures.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no
reason to expect the type of "myRecordID" not to be CKRecordID. (In fact,
it's CKRecord.) There is also no way to clarify what ambiguousName is since
closures don't have argument labels at all... but that's another problem
altogether.

Turning now to example (2), "myString.capitalized(with:myLocale)"
sacrifices BOTH the subject and the prepositional object. We have now lost
any enforced contextual cues, fully orphaning "with", allowing and even
encouraging a final closure argument like:

{ return $0.capitalized(with: .current)) }

What is $0? Reading this, and being familiar with Swift, you will likely
assume it's a string, but will you remember that .current is a Locale? The
compiler knows, but why was it a good idea to strip out that information?

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

We could also have:

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value
pair, array will be a dict, and key will be an index type! This may seem
like a ridiculous example, but I have literally seen things like this.

*DISCUSSION*

Making computer code more like natural language and thus more approachable
to non-computer-science types was clearly one of the primary goals of
Apple's method names in Objective C.

I've been a fan of the general direction in which Swift has taken things,
but I'm starting to see code that is quite unreadable as a result of the
stripping away of useful information from code, as if more white space will
improve our ability to make sense out of our arcane invocations.

The point of code readability is for humans to be able to read and
understand code with minimal extra effort. Explicit information is
important because humans would like to read code without having to dig
through five different headers and read lots of documentation files. This
directly increases costs, because when a new developer comes onto a
project, the longer it takes them to understand the codebase, the more it
will cost the company.

I don't believe the intent with these changes was to increase ambiguity;
at WWDC '16 we were told it was to increase clarity. I'm just not sure
that's the consistent effect we actually received.

What we got instead is more times we will need to CMD-click things in
XCode to get a clue. That's especially annoying because you're at XCode's
mercy as to whether it actually wants to jump to a given declaration or
not. This seems pretty hit or miss in my experience, even within a basic
playground page, and is especially troublesome with jumping to declarations
within CocoaPods. Selecting some code to get documentation in the side
panel seems equally unreliable.

Perhaps CMD-click problems have to do with a project setting, but is there
really a time when you would ever NOT want to have these capabilities? Why
is it tied into a setting? Further, when you're reading through a repo on
GitHub itself, you don't have CMD-click capability; you want the code to be
as clear as possible, without lots of ambiguity.

*From Ambiguous to Cryptic*

Orphaning method signatures by stripping useful return type and argument
type information wouldn't be so bad if variables were all named
descriptively, but that is a strangely optimistic hope for a language
that's as paranoid about safety that it was specifically designed to
prevent many categories of common mistakes.

The increased ambiguity in Swift from the removal of prepositional
components has been further compounded by other forms of ambiguity that are
increasingly everywhere in Swift, and which developers have taken copious
advantage of to the point of abuse. Examples are:

- arguments labelled as "_" reduce code clarity
- calls to functions that take closures as the final argument can leave
the final argument label out, decreasing readability
- closures have lack explicit type information, leaving it up to variable
names to let us know what's going on; however, the use $# syntax and/or
cryptically named variables often leaves it totally ambiguous
- generic types in method signatures using a single letter give no clue as
to the type's purpose
- function types can't have argument labels, which might give a sense of
context or informative cues, even if they added boilerplate
- inferred nested type parents (like the ability to do .current instead of
being forced to do Location.current)

*Why is removing context and clue good?*

Is it possible that this was an overreaction to the perceived
over-verbosity of Objective C? I'm honestly curious. I will try to read
back through the archives, but perhaps someone can give me a good summary.

In the old Objective C style, method names could be quite long, but I
never felt a lack of context that made code unclear. The method signatures
were basically like sentences without spaces, and they seemed to follow
pretty reliable rules. So reading them actually made grammatical sense,
improving code readability. I could always tell what a call was doing.

While I won't deny that Objective C went overboard sometimes in verbosity,
and while few would dispute that we should clean up its style, could it be
we are going too far making Swift encourage code that's ambiguous to
everyone except the compiler? Could it be a mistake for Swift to err so far
on the side of inferring everything and stripping away contextual
information?

A first-time reader of some unseen code is not going to be able to infer
things like the original programmer or the compiler can. This increases the
possibility of bugs (and exploits) sneaking in "under the radar."

*PROPOSAL*

To add more clarity back to Swift, I'd propose the following rules be
applied consistently across the language:

• Every time a preposition shows up in a function/method signature, there
is an explicit subject and object of the preposition, except in the
following exceptions.

• The subject of a preposition can be left out if the function/method is
mutating AND doesn't return a value, such as Array's .append(contentsOf:)
method. This method would become: .append(contentsOfCollection:) so that
it's clear what we're doing here, even within a closure.

• No preposition should be used when it would add no extra meaning between
what’s inside and outside the parentheses. For example, userFont(ofSize
fontSize:) should just be userFont(size: fontSize), because “of” is
completely superfluous here. AVFragmentedMovie’s track(withTrackID:) could
just as easily be track(forTrackID:) or track(TrackID:) which tells us that
the preposition is superfluous and so why not just have it be track(id:).

• If the preposition goes with a verb in the method signature, the verb
should be the last thing before the opening parenthesis (e.g. String's
.capitalized function would now look like
name.stringCapitalized(accordingToLocale: .current)... no pun intended).

• In the case of .forEach, since it’s almost identical in function to
.map, perhaps a solution could be to eliminate .forEach entirely, and
simply say that anytime .map’s result is unused (or assigned to _), then
the compiler will automatically use .forEach functionality to optimize
performance. For example if in Swift 5 you did _ = myArray.map() {
print($0) }, this would be the same as doing myArray.forEach() { print($0)
} currently. As well, _ = could be inferred.

Note: part of the problem is obviously that parameter names are never used
in function calls when there is an argument label, but often, the argument
label is now just a preposition, while its prepositional object is the (now
hidden) parameter name. Perhaps we can think of a novel solution to this?

I'm interested to know what people think about this. Perhaps there are
other ideas or ways to add clarity back in. Thanks for reading this long
message.

Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I guess I will accept that implicit types will serve to explain the purpose of prepositions and do not need to be written explicitly. But I’m curious if there are any other thoughts on the trend exemplified by `fetch(withRecordID:)` and the issue that the “how” is described but not the “what”, which a more serious problem in my eyes, and my proposal that methods like this be of the form `<subject>.<action><object>(<preposition><secondObjectIfNecessary>:)`, where the second object is necessary when type information alone does not convey its meaning, as is the case with a record’s ID.

···

On Aug 2, 2017, at 1:49 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Aug 2, 2017 at 12:00 Robert Bennett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I agree with some of your points and disagree with others. You are right that Swift did not get everything right when it “Swiftified” some Objective-C names. However, I don’t think your criticisms are all correct.

At some point, Swift decided to omit needless “helpful” names from method names. This is a good thing. You don’t “string-capitalize” a string; you capitalize it. And since types are known at compile time, there isn’t really a need to name the type of every argument. This would hurt the readability/Englishness of Swift. For instance, `append(contentsOf:)` — in English, you would say “append the contents of that bucket to my array”, not “append the contents of collection that bucket to my array”. Only collections can have contents so you can omit that type, both in English and in Swift. In short, when the argument label clearly implies the type of the argument, there’s no need to explicitly state it. However when that type is not clear then it should be specified.

I think Swift did move in a weird direction with method base-names, as shown in some of your examples. It relies too heavily on a shared verb among methods with argument labels serving as the primary differentiators, when sometimes part of the label should be attached to the verb. And for arguments, it relies too heavily on type information to understand the functionality; somewhat-hidden type information (inspectable in Xcode, but not very obvious if you’re just reading quickly) should be made visible in the argument labels where necessary.

To your specific examples:

`fetch(withRecordID)` leaves unanswered the question “what is being fetched?”, instead forcing you to infer the answer from the question “how is the thing being fetched, being fetched?” — “it is being fetched by record ID so I guess it must be a record”. Is there even such a thing as a record ID? I think not — there are just IDs, some of which are tied to records. I think that that method should be called `fetchRecord(withID:)`. This makes it clear that a) a record is being fetched and b) its ID is what is used to fetch it.
`string.capitalized(with:)` it very unclear. As an uneducated programmer, I have no idea what you capitalize a string “with”. With a font? With some Unicode machinery? With gusto? (Ok that last one was a joke.) It’s not at all obvious that you capitalize a string with a locale, and hence I think that that method should be called `string.capitalized(withLocale:)`. I explain above why it should be string.capitalized and not string.stringCapitalized.

This topic was reviewed extensively at the beginning of Swift Evolution. The idea is that type information does not need to be repeated in the label. Since the locale is of type Locale, “with” is preferred over “withLocale” because “withLocale: Locale” is redundant. As this has been long approved by the community, it’s not in scope to roll it back.

`dict.remove(at:)` is a bit of a tougher case. It is mutating, so it has to be in the present tense. Yet it does return an element, so maybe it should state that? I think the deciding vote is that it’s @discardableResult, which means it should be treated as primarily removing the element; returning it is secondary. Maybe to indicate the returning of the element, it should be called `dict.pop(at:)`. “Pop" implies the element popped, and probably implies @discardableResult in most programmers’ minds as well.

Brent had a draft proposal to revise the names of collection methods to improve the situation here. There is room for improvement.

`date.timeInterval` is another tricky one. IMO the most natural choice is `date.timeIntervalSince(_:)`. I don’t think the date argument label is necessary because given a date, you can only compute a time interval since… another date. See the `array.append(contentsOf:)` example above. And although `since` is a preposition, it should be part of the method name, and not an argument label, because dates do not just “have” time intervals. A database can fetch a record, but a date does not have a time interval — it has a time interval since (another date).
This should definitely be `forEach`. `array.each` would be another name for `array.everyElementSatisfiesCondition(_ f: (Element)->Bool))`. “For each” specifies that you will do something *for each* element as opposed to asking a question about each element. In English, “for each thing in my bucket…” will probably be followed by “do this thing”. You could say “for each thing in my bucket, it’s true that…” but you’d probably just say “everything in my bucket is…"

So, if I had to come up with my own rules for naming conventions:

Methods should first state what they do, then how they do it. You fetch a record using an ID; you don’t fetch a thing using a record ID. Ask the question “does the calling variable have such-and-such a thing” or “can the calling variable do such-and-such a thing”, and be specific about exactly what the thing is. If yes, than that thing is a good candidate for a method name. A database can fetch, sure, but more specifically it can fetch records, and that’s about as specific as you can get. A date doesn’t have a collection of time intervals sitting around, but it can compute the *time interval since* another date. Strings can be capitalized (with a locale), dictionaries can remove (at an index), arrays can append (the contents of some collection).
When the preposition/argument label makes it clear what sort of argument it takes, then it’s fine to omit the type. However if the argument label is vague, then the argument type should be included. In this case the type is not just a type specifier — it’s really part of the English description. You don’t say “append the contents of collection that bucket to my array”, but any sentence about capitalizing a string with a locale would use the word “locale” explicitly, either in the name of the variable or otherwise — “capitalize that string with your specified locale”, “capitalize that string with locale XYZ”, etc. The English language doesn’t really have the notion of implicitly locale-like objects the way it has implicitly collection-like objects such as buckets, bags, etc, so the fact that a locale is being used should be explicit while the fact that a collection is being used may be implicit.
As an aside, I feel that “using Locale” would be better than “with Locale”, but that’s a conversation for another time.
Three nouns may be required in a name, as in the fetch record example. The calling variable is always the subject, and there may be either one or two objects in play. If there are two, then one should go in the method name; the other may or may not be made explicit in the argument label, depending on clarity (see the above point). When a database fetches a record using its ID, you have only two choices: db.fetchRecord(with:) or db.fetchRecord(withID:). In this case, clarity would lead to the second choice, as you don’t know what you’re fetching the record *with*. In the date example, the calling date is the subject, and it is *producing a time interval since* another date. So again, two choices: date.timeIntervalSince(date:) or date.timeIntervalSince(_:). Since you can only find the time interval between two dates, `date:` can be omitted.

It’s hard to make hard-and-fast rules around this because language is far from hard-and-fast itself. But I think these would be good guidelines.

On Aug 2, 2017, at 11:44 AM, Jon Gilbert via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When Swift 3 launched, it introduced a new concept of placing the preposition inside the parentheses. (See discussion here: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009523.html).

I'm fine with that, however this change got implemented in an inconsistent manner, making Swift and its APIs more vague, thereby decreasing code clarity.

I was hoping to see these inconsistencies get cleared up with Swift 4, however they were not. I realized that perhaps I could have spoken up and taken more of an active role in Swift Evolution. So... better late than never. Here are my thoughts, towards consistency and disambiguation in Swift.

Disclaimer: I’m sure the last thing anyone wants are more changes to the APIs. However, I also don't think these inconsistencies should be left in place forever. I have tried to go back and read through as much of the relevant discussions on list as I could, and it seems like

Please let me some give examples below. After that is some discussion and a proposal.

EXAMPLES

Take for example the preposition "with." A preposition is meaningless without a subject and an object. Look at the following three sentences:

(A) "A boy with the dog Molly walked across the street."

(B) "With the dog Molly walked across the street."

(C) "A boy with Molly walked across the street."

Sentence (A) is longest, but it makes perfect sense.

Sentence (B) is nonsensical and grammatically incorrect because while the word "with" takes two arguments, only one is present. If there was a comma after "dog," then it would make sense; "With the dog, Molly walked across the street" is fine. But if we were to assume that Molly is not the dog, in this case, we'd actually be wrong.

Sentence (C), while grammatically correct, leaves it unclear whether Molly is a dog, a girl, or… something else.

The reason for this is, whenever a preposition is used in English, it almost always takes a dyadic form, relating a subject to the preposition's object. The two most common dyadic formats are:

<subject> [<preposition> <object of preposition>]
<The boy> [<with> <the dog>] crossed the street.

[<preposition> < object of preposition>] <subject>
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature were generally respected in method signatures. However, Swift 3's migration of the preposition inside the parentheses also seems to have been accompanied by the stripping away of either the subject, the prepositional object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
(subject "Record" got removed)

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
(subject "String" and prep. object "Locale" both got removed)

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
(subject "element" already missing from both; prep. object "Index" got removed)

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
(subject "timeInterval" and object "Date" both still present; but oddly, preposition "since" got left outside of the parentheses)

(5) Array:

      old: myArray.forEach({ thing in code})
new: myArray.forEach() { thing in //code }
            (preposition “for” is outside of the parentheses)

DISCUSSION OF EXAMPLES

These four changes are inconsistent with each other regarding whether the subject, prepositional object, or both were removed, and we don't even have consistency as to whether the preposition got moved inside the parentheses (see A below).

As well, these changes generally removed the dyadic arguments to the preposition, which removes cues necessary to disambiguate the prepositional relationship, decreasing code readability (see B below).

(A) Inconsistency

The inconsistency between the examples is shown in the bold text of each example, but lets go over why this matters. It matters because any language is easier to learn the more consistently it sticks to its own rules. Autocomplete is our great savior, but still, if we were being consistent, then the new method signatures would have been:

(1) myCKDatabase.fetchRecord(withRecordID:)
(2) myString.stringCapitalized(withLocale:)
(3) myDictionary.elementRemoved(atIndex:)
(4) myDate.timeInterval(sinceDate:)
(5) myArray.each(inClosure: )

Side note: for plain English readability, we might prefer elementRemoved(fromIndex:) and stringCapitlized(accordingToLocale:).

Although I do understand removing "string" from the latter was to reduce redundancy in function/method declarations, we only make one declaration, yet we make many calls. So increasing ambiguity in calls does not seem like a good trade-off for decreased boilerplate in declarations. More often than not it's calls that we're reading, not the declarations—unless of course the call was ambiguous and we had to read the declaration to make sense out of it. So perhaps we might question if increased ambiguity is an overall good thing.

Side note: example (5), .forEach, does seem like a very exceptional case, and possibly a sacred cow. See the Proposal section for further discussion of this.

(B) Increased Ambiguity

In all of these changes, one of or both parts of the dyadic arguments of the preposition have been excised from the method signatures. This increases ambiguity in Swift 3/4 vs. Objective C and Swift 2, especially in closures, as I will explain below.

In example (1), the old method argument makes grammatical sense because "record with RecordID" follows the format, <subject> [<preposition> <object of preposition>], just like "boy with dog tag" or "cow with black spots." This improves code readability, because when you read this, you know that this function will give you a record matching a particular recordID. However in Swift 3, we have the equivalent of, "with a recordID"—i.e. is *implied* that the thing being fetched is a record.

This isn't a problem you're reading the method signature in the header, and it's not a problem when you're writing the code, because you'll get warnings and errors if you try to assign to the wrong type.

However this removal of explicit contextual cues from the method signature harms readability, since now, the compiler will let people write code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short variable names or NO variable names. However, spend much time on GitHub or in CocoaPods and you will see an increasing number of codebases where that's exactly what they do, especially in closures.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no reason to expect the type of "myRecordID" not to be CKRecordID. (In fact, it's CKRecord.) There is also no way to clarify what ambiguousName is since closures don't have argument labels at all... but that's another problem altogether.

Turning now to example (2), "myString.capitalized(with:myLocale)" sacrifices BOTH the subject and the prepositional object. We have now lost any enforced contextual cues, fully orphaning "with", allowing and even encouraging a final closure argument like:

{ return $0.capitalized(with: .current)) }

What is $0? Reading this, and being familiar with Swift, you will likely assume it's a string, but will you remember that .current is a Locale? The compiler knows, but why was it a good idea to strip out that information?

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

We could also have:

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value pair, array will be a dict, and key will be an index type! This may seem like a ridiculous example, but I have literally seen things like this.

DISCUSSION

Making computer code more like natural language and thus more approachable to non-computer-science types was clearly one of the primary goals of Apple's method names in Objective C.

I've been a fan of the general direction in which Swift has taken things, but I'm starting to see code that is quite unreadable as a result of the stripping away of useful information from code, as if more white space will improve our ability to make sense out of our arcane invocations.

The point of code readability is for humans to be able to read and understand code with minimal extra effort. Explicit information is important because humans would like to read code without having to dig through five different headers and read lots of documentation files. This directly increases costs, because when a new developer comes onto a project, the longer it takes them to understand the codebase, the more it will cost the company.

I don't believe the intent with these changes was to increase ambiguity; at WWDC '16 we were told it was to increase clarity. I'm just not sure that's the consistent effect we actually received.

What we got instead is more times we will need to CMD-click things in XCode to get a clue. That's especially annoying because you're at XCode's mercy as to whether it actually wants to jump to a given declaration or not. This seems pretty hit or miss in my experience, even within a basic playground page, and is especially troublesome with jumping to declarations within CocoaPods. Selecting some code to get documentation in the side panel seems equally unreliable.

Perhaps CMD-click problems have to do with a project setting, but is there really a time when you would ever NOT want to have these capabilities? Why is it tied into a setting? Further, when you're reading through a repo on GitHub itself, you don't have CMD-click capability; you want the code to be as clear as possible, without lots of ambiguity.

From Ambiguous to Cryptic

Orphaning method signatures by stripping useful return type and argument type information wouldn't be so bad if variables were all named descriptively, but that is a strangely optimistic hope for a language that's as paranoid about safety that it was specifically designed to prevent many categories of common mistakes.

The increased ambiguity in Swift from the removal of prepositional components has been further compounded by other forms of ambiguity that are increasingly everywhere in Swift, and which developers have taken copious advantage of to the point of abuse. Examples are:

- arguments labelled as "_" reduce code clarity
- calls to functions that take closures as the final argument can leave the final argument label out, decreasing readability
- closures have lack explicit type information, leaving it up to variable names to let us know what's going on; however, the use $# syntax and/or cryptically named variables often leaves it totally ambiguous
- generic types in method signatures using a single letter give no clue as to the type's purpose
- function types can't have argument labels, which might give a sense of context or informative cues, even if they added boilerplate
- inferred nested type parents (like the ability to do .current instead of being forced to do Location.current)

Why is removing context and clue good?

Is it possible that this was an overreaction to the perceived over-verbosity of Objective C? I'm honestly curious. I will try to read back through the archives, but perhaps someone can give me a good summary.

In the old Objective C style, method names could be quite long, but I never felt a lack of context that made code unclear. The method signatures were basically like sentences without spaces, and they seemed to follow pretty reliable rules. So reading them actually made grammatical sense, improving code readability. I could always tell what a call was doing.

While I won't deny that Objective C went overboard sometimes in verbosity, and while few would dispute that we should clean up its style, could it be we are going too far making Swift encourage code that's ambiguous to everyone except the compiler? Could it be a mistake for Swift to err so far on the side of inferring everything and stripping away contextual information?

A first-time reader of some unseen code is not going to be able to infer things like the original programmer or the compiler can. This increases the possibility of bugs (and exploits) sneaking in "under the radar."

PROPOSAL

To add more clarity back to Swift, I'd propose the following rules be applied consistently across the language:

• Every time a preposition shows up in a function/method signature, there is an explicit subject and object of the preposition, except in the following exceptions.

• The subject of a preposition can be left out if the function/method is mutating AND doesn't return a value, such as Array's .append(contentsOf:) method. This method would become: .append(contentsOfCollection:) so that it's clear what we're doing here, even within a closure.

• No preposition should be used when it would add no extra meaning between what’s inside and outside the parentheses. For example, userFont(ofSize fontSize:) should just be userFont(size: fontSize), because “of” is completely superfluous here. AVFragmentedMovie’s track(withTrackID:) could just as easily be track(forTrackID:) or track(TrackID:) which tells us that the preposition is superfluous and so why not just have it be track(id:).

• If the preposition goes with a verb in the method signature, the verb should be the last thing before the opening parenthesis (e.g. String's .capitalized function would now look like name.stringCapitalized(accordingToLocale: .current)... no pun intended).

• In the case of .forEach, since it’s almost identical in function to .map, perhaps a solution could be to eliminate .forEach entirely, and simply say that anytime .map’s result is unused (or assigned to _), then the compiler will automatically use .forEach functionality to optimize performance. For example if in Swift 5 you did _ = myArray.map() { print($0) }, this would be the same as doing myArray.forEach() { print($0) } currently. As well, _ = could be inferred.

Note: part of the problem is obviously that parameter names are never used in function calls when there is an argument label, but often, the argument label is now just a preposition, while its prepositional object is the (now hidden) parameter name. Perhaps we can think of a novel solution to this?

I'm interested to know what people think about this. Perhaps there are other ideas or ways to add clarity back in. Thanks for reading this long message.

Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I guess I will accept that implicit types will serve to explain the
purpose of prepositions and do not need to be written explicitly. But I’m
curious if there are any other thoughts on the trend exemplified by
`fetch(withRecordID:)` and the issue that the “how” is described but not
the “what”,

`fetch(withRecordID:)` is a CloudKit API not subject to Swift Evolution.
Interestingly, the corresponding Obj-C function is called
`fetchRecordWithID:` and not `fetchWithRecordID:`, so clearly someone at
Apple chose the Swift name manually: a rote translation from Obj-C based on
Swift API guidelines would be `fetchRecord(with:)`, as the argument is of
type `CKRecordID`. This clearly deliberate renaming is not dictated by
Swift naming guidelines but a discretionary choice of the CloudKit authors
at Apple; if you feel this is a bug, then I guess you can file a Radar.

which a more serious problem in my eyes, and my proposal that methods like

···

On Wed, Aug 2, 2017 at 2:23 PM, Robert Bennett <rltbennett@icloud.com> wrote:

this be of the form `<subject>.<action><object>(<preposition><secondObjectIfNecessary>:)`,
where the second object is necessary when type information alone does not
convey its meaning, as is the case with a record’s ID.

On Aug 2, 2017, at 1:49 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Aug 2, 2017 at 12:00 Robert Bennett via swift-evolution < > swift-evolution@swift.org> wrote:

I agree with some of your points and disagree with others. You are right
that Swift did not get everything right when it “Swiftified” some
Objective-C names. However, I don’t think your criticisms are all correct.

At some point, Swift decided to omit needless “helpful” names from method
names. This is a good thing. You don’t “string-capitalize” a string; you
capitalize it. And since types are known at compile time, there isn’t
really a need to name the type of every argument. This would hurt the
readability/Englishness of Swift. For instance, `append(contentsOf:)` — in
English, you would say “append the contents of that bucket to my array”,
not “append the contents of collection that bucket to my array”. Only
collections can have contents so you can omit that type, both in English
and in Swift. In short, when the argument label clearly implies the type of
the argument, there’s no need to explicitly state it. However when that
type is not clear then it should be specified.

I think Swift did move in a weird direction with method base-names, as
shown in some of your examples. It relies too heavily on a shared verb
among methods with argument labels serving as the primary differentiators,
when sometimes part of the label should be attached to the verb. And for
arguments, it relies too heavily on type information to understand the
functionality; somewhat-hidden type information (inspectable in Xcode, but
not very obvious if you’re just reading quickly) should be made visible in
the argument labels where necessary.

To your specific examples:

   1. `fetch(withRecordID)` leaves unanswered the question “what is
   being fetched?”, instead forcing you to infer the answer from the question
   “how is the thing being fetched, being fetched?” — “it is being fetched by
   record ID so I guess it must be a record”. Is there even such a thing as a
   record ID? I think not — there are just IDs, some of which are tied to
   records. I think that that method should be called `fetchRecord(withID:)`.
   This makes it clear that a) a record is being fetched and b) its ID is what
   is used to fetch it.
   2. `string.capitalized(with:)` it very unclear. As an uneducated
   programmer, I have no idea what you capitalize a string “with”. With a
   font? With some Unicode machinery? With gusto? (Ok that last one was a
   joke.) It’s not at all obvious that you capitalize a string with a locale,
   and hence I think that that method should be called `string.capitalized(withLocale:)`.
   I explain above why it should be string.capitalized and not
   string.stringCapitalized.

This topic was reviewed extensively at the beginning of Swift Evolution.
The idea is that type information does not need to be repeated in the
label. Since the locale is of type Locale, “with” is preferred over
“withLocale” because “withLocale: Locale” is redundant. As this has been
long approved by the community, it’s not in scope to roll it back.

   1.
   2. `dict.remove(at:)` is a bit of a tougher case. It is mutating, so
   it has to be in the present tense. Yet it does return an element, so maybe
   it should state that? I think the deciding vote is that it’s
   @discardableResult, which means it should be treated as primarily removing
   the element; returning it is secondary. Maybe to indicate the returning of
   the element, it should be called `dict.pop(at:)`. “Pop" implies the element
   popped, and probably implies @discardableResult in most programmers’ minds
   as well.

Brent had a draft proposal to revise the names of collection methods to
improve the situation here. There is room for improvement.

   1.
   2. `date.timeInterval` is another tricky one. IMO the most natural
   choice is `date.timeIntervalSince(_:)`. I don’t think the date argument
   label is necessary because given a date, you can only compute a time
   interval since… another date. See the `array.append(contentsOf:)` example
   above. And although `since` is a preposition, it should be part of the
   method name, and not an argument label, because dates do not just “have”
   time intervals. A database can fetch a record, but a date does not have a
   time interval — it has a time interval since (another date).
   3. This should definitely be `forEach`. `array.each` would be another
   name for `array.everyElementSatisfiesCondition(_ f:
   (Element)->Bool))`. “For each” specifies that you will do something *for
   each* element as opposed to asking a question about each element. In
   English, “for each thing in my bucket…” will probably be followed by “do
   this thing”. You could say “for each thing in my bucket, it’s true that…”
   but you’d probably just say “everything in my bucket is…"

So, if I had to come up with my own rules for naming conventions:

   1. Methods should first state what they do, then how they do it. You
   fetch a record using an ID; you don’t fetch a thing using a record ID. Ask
   the question “does the calling variable have such-and-such a thing” or “can
   the calling variable do such-and-such a thing”, and be specific about
   exactly what the thing is. If yes, than that thing is a good candidate for
   a method name. A database can fetch, sure, but more specifically it can
   fetch records, and that’s about as specific as you can get. A date doesn’t
   have a collection of time intervals sitting around, but it can compute the
   *time interval since* another date. Strings can be capitalized (with a
   locale), dictionaries can remove (at an index), arrays can append (the
   contents of some collection).
   2. When the preposition/argument label makes it clear what sort of
   argument it takes, then it’s fine to omit the type. However if the argument
   label is vague, then the argument type should be included. In this case the
   type is not just a type specifier — it’s really part of the English
   description. You don’t say “append the contents of collection that bucket
   to my array”, but any sentence about capitalizing a string with a locale
   would use the word “locale” explicitly, either in the name of the variable
   or otherwise — “capitalize that string with your specified locale”,
   “capitalize that string with locale XYZ”, etc. The English language doesn’t
   really have the notion of implicitly locale-like objects the way it has
   implicitly collection-like objects such as buckets, bags, etc, so the fact
   that a locale is being used should be explicit while the fact that a
   collection is being used may be implicit.
   As an aside, I feel that “using Locale” would be better than “with
   Locale”, but that’s a conversation for another time.
   3. Three nouns may be required in a name, as in the fetch record
   example. The calling variable is always the subject, and there may be
   either one or two objects in play. If there are two, then one should go in
   the method name; the other may or may not be made explicit in the argument
   label, depending on clarity (see the above point). When a database fetches
   a record using its ID, you have only two choices: db.fetchRecord(with:) or
   db.fetchRecord(withID:). In this case, clarity would lead to the second
   choice, as you don’t know what you’re fetching the record *with*. In the
   date example, the calling date is the subject, and it is *producing a time
   interval since* another date. So again, two choices:
   date.timeIntervalSince(date:) or date.timeIntervalSince(_:). Since you can
   only find the time interval between two dates, `date:` can be omitted.

It’s hard to make hard-and-fast rules around this because language is far
from hard-and-fast itself. But I think these would be good guidelines.

On Aug 2, 2017, at 11:44 AM, Jon Gilbert via swift-evolution < >> swift-evolution@swift.org> wrote:

When Swift 3 launched, it introduced a new concept of placing the
preposition inside the parentheses. (See discussion here:
https://lists.swift.org/pipermail/swift-evolution/
Week-of-Mon-20160208/009523.html).

I'm fine with that, however this change got implemented in an
inconsistent manner, making Swift and its APIs more vague, thereby
decreasing code clarity.

I was hoping to see these inconsistencies get cleared up with Swift 4,
however they were not. I realized that perhaps I could have spoken up and
taken more of an active role in Swift Evolution. So... better late than
never. Here are my thoughts, towards consistency and disambiguation in
Swift.

Disclaimer: I’m sure the last thing anyone wants are more changes to the
APIs. However, I also don't think these inconsistencies should be left in
place forever. I have tried to go back and read through as much of the
relevant discussions on list as I could, and it seems like

Please let me some give examples below. After that is some discussion and
a proposal.

*EXAMPLES*

Take for example the preposition "with." A preposition is meaningless
without a subject and an object. Look at the following three sentences:

(A) "A boy with the dog Molly walked across the street."

(B) "With the dog Molly walked across the street."

(C) "A boy with Molly walked across the street."

Sentence (A) is longest, but it makes perfect sense.

Sentence (B) is nonsensical and grammatically incorrect because while the
word "with" takes two arguments, only one is present. If there was a comma
after "dog," then it would make sense; "With the dog, Molly walked across
the street" is fine. But if we were to assume that Molly is not the dog, in
this case, we'd actually be wrong.

Sentence (C), while grammatically correct, leaves it unclear whether
Molly is a dog, a girl, or… something else.

The reason for this is, whenever a preposition is used in English, it
almost always takes a dyadic form, relating a subject to the preposition's
object. The two most common dyadic formats are:

*<subject> [<preposition> <object of preposition>]*
<The boy> [<with> <the dog>] crossed the street.

*[<preposition> <** object of preposition**>] <subject>*
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature
were generally respected in method signatures. However, Swift 3's migration
of the preposition inside the parentheses also seems to have been
accompanied by the stripping away of either the subject, the prepositional
object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
*(subject "Record" got removed)*

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
*(subject "String" and prep. object "Locale" both got removed)*

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
*(subject "element" already missing from both; prep. object "Index" got
removed)*

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
*(subject "timeInterval" and object "Date" both still present; but oddly,
preposition "since" got left outside of the parentheses)*

(5) Array:

    old: myArray.forEach({ thing in code})

new: myArray.forEach() { thing in //code }

            *(preposition “for” is outside of the parentheses)*

*DISCUSSION OF EXAMPLES*

These four changes are inconsistent with each other regarding whether the
subject, prepositional object, or both were removed, and we don't even have
consistency as to whether the preposition got moved inside the parentheses
(see A below).

As well, these changes generally removed the dyadic arguments to the
preposition, which removes cues necessary to disambiguate the prepositional
relationship, decreasing code readability (see B below).

*(A) Inconsistency*

The inconsistency between the examples is shown in the bold text of each
example, but lets go over why this matters. It matters because any language
is easier to learn the more consistently it sticks to its own rules.
Autocomplete is our great savior, but still, if we were being consistent,
then the new method signatures would have been:

(1) myCKDatabase.fetchRecord(withRecordID:)
(2) myString.stringCapitalized(withLocale:)
(3) myDictionary.elementRemoved(atIndex:)
(4) myDate.timeInterval(sinceDate:)
(5) myArray.each(inClosure: )

Side note: for plain English readability, we might prefer
elementRemoved(fromIndex:) and stringCapitlized(accordingToLocale:).

Although I do understand removing "string" from the latter was to reduce
redundancy in function/method declarations, we only make one declaration,
yet we make many calls. So increasing ambiguity in calls does not seem like
a good trade-off for decreased boilerplate in declarations. More often than
not it's calls that we're reading, not the declarations—unless of course
the call was ambiguous and we had to read the declaration to make sense out
of it. So perhaps we might question if increased ambiguity is an overall
good thing.

Side note: example (5), .forEach, does seem like a very exceptional case,
and possibly a sacred cow. See the Proposal section for further discussion
of this.

*(B) Increased Ambiguity*

In all of these changes, one of or both parts of the dyadic arguments of
the preposition have been excised from the method signatures. This
increases ambiguity in Swift 3/4 vs. Objective C and Swift 2, especially in
closures, as I will explain below.

In example (1), the old method argument makes grammatical sense because
"record with RecordID" follows the format, *<subject> [<preposition>
<object of preposition>]*, just like "boy with dog tag" or "cow with
black spots." This improves code readability, because when you read this,
you know that this function will give you a record matching a particular
recordID. However in Swift 3, we have the equivalent of, "with a
recordID"—i.e. is *implied* that the thing being fetched is a record.

This isn't a problem you're reading the method signature in the header,
and it's not a problem when you're writing the code, because you'll get
warnings and errors if you try to assign to the wrong type.

However this removal of explicit contextual cues from the method
signature harms readability, since now, the compiler will let people write
code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short
variable names or NO variable names. However, spend much time on GitHub or
in CocoaPods and you will see an increasing number of codebases where
that's exactly what they do, especially in closures.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no
reason to expect the type of "myRecordID" not to be CKRecordID. (In fact,
it's CKRecord.) There is also no way to clarify what ambiguousName is since
closures don't have argument labels at all... but that's another problem
altogether.

Turning now to example (2), "myString.capitalized(with:myLocale)"
sacrifices BOTH the subject and the prepositional object. We have now lost
any enforced contextual cues, fully orphaning "with", allowing and even
encouraging a final closure argument like:

{ return $0.capitalized(with: .current)) }

What is $0? Reading this, and being familiar with Swift, you will likely
assume it's a string, but will you remember that .current is a Locale? The
compiler knows, but why was it a good idea to strip out that information?

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

We could also have:

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value
pair, array will be a dict, and key will be an index type! This may seem
like a ridiculous example, but I have literally seen things like this.

*DISCUSSION*

Making computer code more like natural language and thus more
approachable to non-computer-science types was clearly one of the primary
goals of Apple's method names in Objective C.

I've been a fan of the general direction in which Swift has taken things,
but I'm starting to see code that is quite unreadable as a result of the
stripping away of useful information from code, as if more white space will
improve our ability to make sense out of our arcane invocations.

The point of code readability is for humans to be able to read and
understand code with minimal extra effort. Explicit information is
important because humans would like to read code without having to dig
through five different headers and read lots of documentation files. This
directly increases costs, because when a new developer comes onto a
project, the longer it takes them to understand the codebase, the more it
will cost the company.

I don't believe the intent with these changes was to increase ambiguity;
at WWDC '16 we were told it was to increase clarity. I'm just not sure
that's the consistent effect we actually received.

What we got instead is more times we will need to CMD-click things in
XCode to get a clue. That's especially annoying because you're at XCode's
mercy as to whether it actually wants to jump to a given declaration or
not. This seems pretty hit or miss in my experience, even within a basic
playground page, and is especially troublesome with jumping to declarations
within CocoaPods. Selecting some code to get documentation in the side
panel seems equally unreliable.

Perhaps CMD-click problems have to do with a project setting, but is
there really a time when you would ever NOT want to have these
capabilities? Why is it tied into a setting? Further, when you're reading
through a repo on GitHub itself, you don't have CMD-click capability; you
want the code to be as clear as possible, without lots of ambiguity.

*From Ambiguous to Cryptic*

Orphaning method signatures by stripping useful return type and argument
type information wouldn't be so bad if variables were all named
descriptively, but that is a strangely optimistic hope for a language
that's as paranoid about safety that it was specifically designed to
prevent many categories of common mistakes.

The increased ambiguity in Swift from the removal of prepositional
components has been further compounded by other forms of ambiguity that are
increasingly everywhere in Swift, and which developers have taken copious
advantage of to the point of abuse. Examples are:

- arguments labelled as "_" reduce code clarity
- calls to functions that take closures as the final argument can leave
the final argument label out, decreasing readability
- closures have lack explicit type information, leaving it up to variable
names to let us know what's going on; however, the use $# syntax and/or
cryptically named variables often leaves it totally ambiguous
- generic types in method signatures using a single letter give no clue
as to the type's purpose
- function types can't have argument labels, which might give a sense of
context or informative cues, even if they added boilerplate
- inferred nested type parents (like the ability to do .current instead
of being forced to do Location.current)

*Why is removing context and clue good?*

Is it possible that this was an overreaction to the perceived
over-verbosity of Objective C? I'm honestly curious. I will try to read
back through the archives, but perhaps someone can give me a good summary.

In the old Objective C style, method names could be quite long, but I
never felt a lack of context that made code unclear. The method signatures
were basically like sentences without spaces, and they seemed to follow
pretty reliable rules. So reading them actually made grammatical sense,
improving code readability. I could always tell what a call was doing.

While I won't deny that Objective C went overboard sometimes in
verbosity, and while few would dispute that we should clean up its style,
could it be we are going too far making Swift encourage code that's
ambiguous to everyone except the compiler? Could it be a mistake for Swift
to err so far on the side of inferring everything and stripping away
contextual information?

A first-time reader of some unseen code is not going to be able to infer
things like the original programmer or the compiler can. This increases the
possibility of bugs (and exploits) sneaking in "under the radar."

*PROPOSAL*

To add more clarity back to Swift, I'd propose the following rules be
applied consistently across the language:

• Every time a preposition shows up in a function/method signature, there
is an explicit subject and object of the preposition, except in the
following exceptions.

• The subject of a preposition can be left out if the function/method is
mutating AND doesn't return a value, such as Array's .append(contentsOf:)
method. This method would become: .append(contentsOfCollection:) so that
it's clear what we're doing here, even within a closure.

• No preposition should be used when it would add no extra meaning
between what’s inside and outside the parentheses. For example,
userFont(ofSize fontSize:) should just be userFont(size: fontSize), because
“of” is completely superfluous here. AVFragmentedMovie’s
track(withTrackID:) could just as easily be track(forTrackID:) or
track(TrackID:) which tells us that the preposition is superfluous and so
why not just have it be track(id:).

• If the preposition goes with a verb in the method signature, the verb
should be the last thing before the opening parenthesis (e.g. String's
.capitalized function would now look like name.stringCapitalized(accordingToLocale:
.current)... no pun intended).

• In the case of .forEach, since it’s almost identical in function to
.map, perhaps a solution could be to eliminate .forEach entirely, and
simply say that anytime .map’s result is unused (or assigned to _), then
the compiler will automatically use .forEach functionality to optimize
performance. For example if in Swift 5 you did _ = myArray.map() {
print($0) }, this would be the same as doing myArray.forEach() { print($0)
} currently. As well, _ = could be inferred.

Note: part of the problem is obviously that parameter names are never
used in function calls when there is an argument label, but often, the
argument label is now just a preposition, while its prepositional object is
the (now hidden) parameter name. Perhaps we can think of a novel solution
to this?

I'm interested to know what people think about this. Perhaps there are
other ideas or ways to add clarity back in. Thanks for reading this long
message.

Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

...

This topic was reviewed extensively at the beginning of Swift Evolution.
The idea is that type information does not need to be repeated in the
label. Since the locale is of type Locale, “with” is preferred over
“withLocale” because “withLocale: Locale” is redundant. As this has been
long approved by the community, it’s not in scope to roll it back.

Thanks to everyone who contributed to the list to make the Swift 3 API
guidelines possible. I think the Swift 3 API Guidelines were a tremendous
accomplishment, and I appreciate the actual beauty that can result from its
sparse code.

It seems reasonable that 1 1/2 years after the proposal, more than 1 year
after beta release, and after seeing the API Guidelines applied out in the
world (or not) and applying them ourselves (or not), to bring up the
subject again and ask if it can be improved. I think Jon gave some good
examples of weaknesses that can result, and Robert gave some good
suggestions.

Changing existing code is probably out of scope to avoid language
churn--and it definitely would contradict Swift 4's goal of preserving
source stability. The Great Re-Renaming? No thanks. (Thought I think The
Great Renaming unfairly got the blame for later migration issues.) Still,
these messages today were substantive suggestions for how to improve Swift
API naming conventions. I am re-reading the threads from Jan and Feb 2016,
which seems appropriate to inform a discussion that builds on them, but it
would be weird to regard those conversations as a reason to shut down any
mention of the subject ever again. This seems like exactly the kind of
subject that should be revisited, and could form a proposal later in the
fall.

As someone who admires the API guidelines and how they make Swift read
elegantly, I want to make them even better and easier to use. I myself
think that the English language is too ungainly to support rules for a
language as elegant as Swift. So I hope we can continue to have this
conversation.

Mike Sanderson

···

On Wed, Aug 2, 2017 at 1:49 PM, Xiaodi Wu via swift-evolution < swift-evolution@swift.org> wrote:

   1.
   2. `dict.remove(at:)` is a bit of a tougher case. It is mutating, so
   it has to be in the present tense. Yet it does return an element, so maybe
   it should state that? I think the deciding vote is that it’s
   @discardableResult, which means it should be treated as primarily removing
   the element; returning it is secondary. Maybe to indicate the returning of
   the element, it should be called `dict.pop(at:)`. “Pop" implies the element
   popped, and probably implies @discardableResult in most programmers’ minds
   as well.

Brent had a draft proposal to revise the names of collection methods to
improve the situation here. There is room for improvement.

   1.
   2. `date.timeInterval` is another tricky one. IMO the most natural
   choice is `date.timeIntervalSince(_:)`. I don’t think the date argument
   label is necessary because given a date, you can only compute a time
   interval since… another date. See the `array.append(contentsOf:)` example
   above. And although `since` is a preposition, it should be part of the
   method name, and not an argument label, because dates do not just “have”
   time intervals. A database can fetch a record, but a date does not have a
   time interval — it has a time interval since (another date).
   3. This should definitely be `forEach`. `array.each` would be another
   name for `array.everyElementSatisfiesCondition(_ f:
   (Element)->Bool))`. “For each” specifies that you will do something *for
   each* element as opposed to asking a question about each element. In
   English, “for each thing in my bucket…” will probably be followed by “do
   this thing”. You could say “for each thing in my bucket, it’s true that…”
   but you’d probably just say “everything in my bucket is…"

So, if I had to come up with my own rules for naming conventions:

   1. Methods should first state what they do, then how they do it. You
   fetch a record using an ID; you don’t fetch a thing using a record ID. Ask
   the question “does the calling variable have such-and-such a thing” or “can
   the calling variable do such-and-such a thing”, and be specific about
   exactly what the thing is. If yes, than that thing is a good candidate for
   a method name. A database can fetch, sure, but more specifically it can
   fetch records, and that’s about as specific as you can get. A date doesn’t
   have a collection of time intervals sitting around, but it can compute the
   *time interval since* another date. Strings can be capitalized (with a
   locale), dictionaries can remove (at an index), arrays can append (the
   contents of some collection).
   2. When the preposition/argument label makes it clear what sort of
   argument it takes, then it’s fine to omit the type. However if the argument
   label is vague, then the argument type should be included. In this case the
   type is not just a type specifier — it’s really part of the English
   description. You don’t say “append the contents of collection that bucket
   to my array”, but any sentence about capitalizing a string with a locale
   would use the word “locale” explicitly, either in the name of the variable
   or otherwise — “capitalize that string with your specified locale”,
   “capitalize that string with locale XYZ”, etc. The English language doesn’t
   really have the notion of implicitly locale-like objects the way it has
   implicitly collection-like objects such as buckets, bags, etc, so the fact
   that a locale is being used should be explicit while the fact that a
   collection is being used may be implicit.
   As an aside, I feel that “using Locale” would be better than “with
   Locale”, but that’s a conversation for another time.
   3. Three nouns may be required in a name, as in the fetch record
   example. The calling variable is always the subject, and there may be
   either one or two objects in play. If there are two, then one should go in
   the method name; the other may or may not be made explicit in the argument
   label, depending on clarity (see the above point). When a database fetches
   a record using its ID, you have only two choices: db.fetchRecord(with:) or
   db.fetchRecord(withID:). In this case, clarity would lead to the second
   choice, as you don’t know what you’re fetching the record *with*. In the
   date example, the calling date is the subject, and it is *producing a time
   interval since* another date. So again, two choices:
   date.timeIntervalSince(date:) or date.timeIntervalSince(_:). Since you can
   only find the time interval between two dates, `date:` can be omitted.

It’s hard to make hard-and-fast rules around this because language is far
from hard-and-fast itself. But I think these would be good guidelines.

On Aug 2, 2017, at 11:44 AM, Jon Gilbert via swift-evolution < >> swift-evolution@swift.org> wrote:

When Swift 3 launched, it introduced a new concept of placing the
preposition inside the parentheses. (See discussion here:
https://lists.swift.org/pipermail/swift-evolution/Week
-of-Mon-20160208/009523.html).

I'm fine with that, however this change got implemented in an
inconsistent manner, making Swift and its APIs more vague, thereby
decreasing code clarity.

I was hoping to see these inconsistencies get cleared up with Swift 4,
however they were not. I realized that perhaps I could have spoken up and
taken more of an active role in Swift Evolution. So... better late than
never. Here are my thoughts, towards consistency and disambiguation in
Swift.

Disclaimer: I’m sure the last thing anyone wants are more changes to the
APIs. However, I also don't think these inconsistencies should be left in
place forever. I have tried to go back and read through as much of the
relevant discussions on list as I could, and it seems like

Please let me some give examples below. After that is some discussion and
a proposal.

*EXAMPLES*

Take for example the preposition "with." A preposition is meaningless
without a subject and an object. Look at the following three sentences:

(A) "A boy with the dog Molly walked across the street."

(B) "With the dog Molly walked across the street."

(C) "A boy with Molly walked across the street."

Sentence (A) is longest, but it makes perfect sense.

Sentence (B) is nonsensical and grammatically incorrect because while the
word "with" takes two arguments, only one is present. If there was a comma
after "dog," then it would make sense; "With the dog, Molly walked across
the street" is fine. But if we were to assume that Molly is not the dog, in
this case, we'd actually be wrong.

Sentence (C), while grammatically correct, leaves it unclear whether
Molly is a dog, a girl, or… something else.

The reason for this is, whenever a preposition is used in English, it
almost always takes a dyadic form, relating a subject to the preposition's
object. The two most common dyadic formats are:

*<subject> [<preposition> <object of preposition>]*
<The boy> [<with> <the dog>] crossed the street.

*[<preposition> <** object of preposition**>] <subject>*
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature
were generally respected in method signatures. However, Swift 3's migration
of the preposition inside the parentheses also seems to have been
accompanied by the stripping away of either the subject, the prepositional
object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
*(subject "Record" got removed)*

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
*(subject "String" and prep. object "Locale" both got removed)*

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
*(subject "element" already missing from both; prep. object "Index" got
removed)*

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
*(subject "timeInterval" and object "Date" both still present; but oddly,
preposition "since" got left outside of the parentheses)*

(5) Array:

    old: myArray.forEach({ thing in code})

new: myArray.forEach() { thing in //code }

            *(preposition “for” is outside of the parentheses)*

*DISCUSSION OF EXAMPLES*

These four changes are inconsistent with each other regarding whether the
subject, prepositional object, or both were removed, and we don't even have
consistency as to whether the preposition got moved inside the parentheses
(see A below).

As well, these changes generally removed the dyadic arguments to the
preposition, which removes cues necessary to disambiguate the prepositional
relationship, decreasing code readability (see B below).

*(A) Inconsistency*

The inconsistency between the examples is shown in the bold text of each
example, but lets go over why this matters. It matters because any language
is easier to learn the more consistently it sticks to its own rules.
Autocomplete is our great savior, but still, if we were being consistent,
then the new method signatures would have been:

(1) myCKDatabase.fetchRecord(withRecordID:)
(2) myString.stringCapitalized(withLocale:)
(3) myDictionary.elementRemoved(atIndex:)
(4) myDate.timeInterval(sinceDate:)
(5) myArray.each(inClosure: )

Side note: for plain English readability, we might prefer
elementRemoved(fromIndex:) and stringCapitlized(accordingToLocale:).

Although I do understand removing "string" from the latter was to reduce
redundancy in function/method declarations, we only make one declaration,
yet we make many calls. So increasing ambiguity in calls does not seem like
a good trade-off for decreased boilerplate in declarations. More often than
not it's calls that we're reading, not the declarations—unless of course
the call was ambiguous and we had to read the declaration to make sense out
of it. So perhaps we might question if increased ambiguity is an overall
good thing.

Side note: example (5), .forEach, does seem like a very exceptional case,
and possibly a sacred cow. See the Proposal section for further discussion
of this.

*(B) Increased Ambiguity*

In all of these changes, one of or both parts of the dyadic arguments of
the preposition have been excised from the method signatures. This
increases ambiguity in Swift 3/4 vs. Objective C and Swift 2, especially in
closures, as I will explain below.

In example (1), the old method argument makes grammatical sense because
"record with RecordID" follows the format, *<subject> [<preposition>
<object of preposition>]*, just like "boy with dog tag" or "cow with
black spots." This improves code readability, because when you read this,
you know that this function will give you a record matching a particular
recordID. However in Swift 3, we have the equivalent of, "with a
recordID"—i.e. is *implied* that the thing being fetched is a record.

This isn't a problem you're reading the method signature in the header,
and it's not a problem when you're writing the code, because you'll get
warnings and errors if you try to assign to the wrong type.

However this removal of explicit contextual cues from the method
signature harms readability, since now, the compiler will let people write
code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short
variable names or NO variable names. However, spend much time on GitHub or
in CocoaPods and you will see an increasing number of codebases where
that's exactly what they do, especially in closures.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no
reason to expect the type of "myRecordID" not to be CKRecordID. (In fact,
it's CKRecord.) There is also no way to clarify what ambiguousName is since
closures don't have argument labels at all... but that's another problem
altogether.

Turning now to example (2), "myString.capitalized(with:myLocale)"
sacrifices BOTH the subject and the prepositional object. We have now lost
any enforced contextual cues, fully orphaning "with", allowing and even
encouraging a final closure argument like:

{ return $0.capitalized(with: .current)) }

What is $0? Reading this, and being familiar with Swift, you will likely
assume it's a string, but will you remember that .current is a Locale? The
compiler knows, but why was it a good idea to strip out that information?

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

We could also have:

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value
pair, array will be a dict, and key will be an index type! This may seem
like a ridiculous example, but I have literally seen things like this.

*DISCUSSION*

Making computer code more like natural language and thus more
approachable to non-computer-science types was clearly one of the primary
goals of Apple's method names in Objective C.

I've been a fan of the general direction in which Swift has taken things,
but I'm starting to see code that is quite unreadable as a result of the
stripping away of useful information from code, as if more white space will
improve our ability to make sense out of our arcane invocations.

The point of code readability is for humans to be able to read and
understand code with minimal extra effort. Explicit information is
important because humans would like to read code without having to dig
through five different headers and read lots of documentation files. This
directly increases costs, because when a new developer comes onto a
project, the longer it takes them to understand the codebase, the more it
will cost the company.

I don't believe the intent with these changes was to increase ambiguity;
at WWDC '16 we were told it was to increase clarity. I'm just not sure
that's the consistent effect we actually received.

What we got instead is more times we will need to CMD-click things in
XCode to get a clue. That's especially annoying because you're at XCode's
mercy as to whether it actually wants to jump to a given declaration or
not. This seems pretty hit or miss in my experience, even within a basic
playground page, and is especially troublesome with jumping to declarations
within CocoaPods. Selecting some code to get documentation in the side
panel seems equally unreliable.

Perhaps CMD-click problems have to do with a project setting, but is
there really a time when you would ever NOT want to have these
capabilities? Why is it tied into a setting? Further, when you're reading
through a repo on GitHub itself, you don't have CMD-click capability; you
want the code to be as clear as possible, without lots of ambiguity.

*From Ambiguous to Cryptic*

Orphaning method signatures by stripping useful return type and argument
type information wouldn't be so bad if variables were all named
descriptively, but that is a strangely optimistic hope for a language
that's as paranoid about safety that it was specifically designed to
prevent many categories of common mistakes.

The increased ambiguity in Swift from the removal of prepositional
components has been further compounded by other forms of ambiguity that are
increasingly everywhere in Swift, and which developers have taken copious
advantage of to the point of abuse. Examples are:

- arguments labelled as "_" reduce code clarity
- calls to functions that take closures as the final argument can leave
the final argument label out, decreasing readability
- closures have lack explicit type information, leaving it up to variable
names to let us know what's going on; however, the use $# syntax and/or
cryptically named variables often leaves it totally ambiguous
- generic types in method signatures using a single letter give no clue
as to the type's purpose
- function types can't have argument labels, which might give a sense of
context or informative cues, even if they added boilerplate
- inferred nested type parents (like the ability to do .current instead
of being forced to do Location.current)

*Why is removing context and clue good?*

Is it possible that this was an overreaction to the perceived
over-verbosity of Objective C? I'm honestly curious. I will try to read
back through the archives, but perhaps someone can give me a good summary.

In the old Objective C style, method names could be quite long, but I
never felt a lack of context that made code unclear. The method signatures
were basically like sentences without spaces, and they seemed to follow
pretty reliable rules. So reading them actually made grammatical sense,
improving code readability. I could always tell what a call was doing.

While I won't deny that Objective C went overboard sometimes in
verbosity, and while few would dispute that we should clean up its style,
could it be we are going too far making Swift encourage code that's
ambiguous to everyone except the compiler? Could it be a mistake for Swift
to err so far on the side of inferring everything and stripping away
contextual information?

A first-time reader of some unseen code is not going to be able to infer
things like the original programmer or the compiler can. This increases the
possibility of bugs (and exploits) sneaking in "under the radar."

*PROPOSAL*

To add more clarity back to Swift, I'd propose the following rules be
applied consistently across the language:

• Every time a preposition shows up in a function/method signature, there
is an explicit subject and object of the preposition, except in the
following exceptions.

• The subject of a preposition can be left out if the function/method is
mutating AND doesn't return a value, such as Array's .append(contentsOf:)
method. This method would become: .append(contentsOfCollection:) so that
it's clear what we're doing here, even within a closure.

• No preposition should be used when it would add no extra meaning
between what’s inside and outside the parentheses. For example,
userFont(ofSize fontSize:) should just be userFont(size: fontSize), because
“of” is completely superfluous here. AVFragmentedMovie’s
track(withTrackID:) could just as easily be track(forTrackID:) or
track(TrackID:) which tells us that the preposition is superfluous and so
why not just have it be track(id:).

• If the preposition goes with a verb in the method signature, the verb
should be the last thing before the opening parenthesis (e.g. String's
.capitalized function would now look like name.stringCapitalized(accordingToLocale:
.current)... no pun intended).

• In the case of .forEach, since it’s almost identical in function to
.map, perhaps a solution could be to eliminate .forEach entirely, and
simply say that anytime .map’s result is unused (or assigned to _), then
the compiler will automatically use .forEach functionality to optimize
performance. For example if in Swift 5 you did _ = myArray.map() {
print($0) }, this would be the same as doing myArray.forEach() { print($0)
} currently. As well, _ = could be inferred.

Note: part of the problem is obviously that parameter names are never
used in function calls when there is an argument label, but often, the
argument label is now just a preposition, while its prepositional object is
the (now hidden) parameter name. Perhaps we can think of a novel solution
to this?

I'm interested to know what people think about this. Perhaps there are
other ideas or ways to add clarity back in. Thanks for reading this long
message.

Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Brent had a draft proposal to revise the names of collection methods to improve the situation here. There is room for improvement.

It didn't change `remove(at:)`, though. (Well, it might have affected its return value or something, I don't remember.) It mostly addressed the `dropLast()`, `prefix(_:)` etc. calls.

To respond to the original post:

Some of the APIs you cite are not very well named, but I think your diagnosis is incorrect and so your prescription isn't going to help.

The reason for this is, whenever a preposition is used in English, it almost always takes a dyadic form, relating a subject to the preposition's object. The two most common dyadic formats are:

<subject> [<preposition> <object of preposition>]
<The boy> [<with> <the dog>] crossed the street.

[<preposition> < object of preposition>] <subject>
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature were generally respected in method signatures. However, Swift 3's migration of the preposition inside the parentheses also seems to have been accompanied by the stripping away of either the subject, the prepositional object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
(subject "Record" got removed)

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
(subject "String" and prep. object "Locale" both got removed)

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
(subject "element" already missing from both; prep. object "Index" got removed)

The thing is, the subjects and prepositional objects *are* present in all of these—they are the parameters and targets of the calls.

In your own example, you say "In space, no one can hear you scream", not "In location space, group-of-people no one can hear you scream". So why is it a problem that we say "myString, capitalized with myLocale" instead of "myString, string capitalized with locale myLocale"? These are redundancies that we would never tolerate in natural language; I don't see why you think code should be different.

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
(subject "timeInterval" and object "Date" both still present; but oddly, preposition "since" got left outside of the parentheses)

This is actually inaccurate—the parameter to `timeIntervalSince(_:)` is unlabeled, so it's:

    new: myDate.timeIntervalSince(myDate)

(5) Array:

      old: myArray.forEach({ thing in code})
new: myArray.forEach() { thing in //code }
            (preposition “for” is outside of the parentheses)

Yes, because the preposition does not apply to the parameter—it applies to the operation as a whole. I'll have more to say on this in a moment.

The inconsistency between the examples is shown in the bold text of each example, but lets go over why this matters. It matters because any language is easier to learn the more consistently it sticks to its own rules.

This is true, but you aren't just proposing sticking more closely to our existing standards—you're proposing *changing* our standards. And I don't think the changes you propose are an improvement. In fact, I'd say each of these examples is worse:

(1) myCKDatabase.fetchRecord(withRecordID:)

"Fetch record with record ID"? I mean, you could at least remove the `Record` before `ID`. What other ID do you suppose it would be?

I *can* see the case for going back to `fetchRecord` instead of just `fetch`, though. On the other hand, I do think that, if you know it's a database, the most obvious thing for `fetch` to be fetching is a record from that database. It's not a dog—it won't be fetching a stick.

(2) myString.stringCapitalized(withLocale:)

Let's translate this to an actual use site, which is what we care about.

  func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sections[section].title.stringCapitalized(withLocale: .current)
  }

What is `string` contributing here? We already know it's a "title", which sounds a lot like a string. If you asked for a "capitalized" string, what else would you get back if not another string?

The locale parameter is a little more tricky. You're right that `(with: .current)` is vague, but I can imagine plenty of call sites where `with` wouldn't be:

  title.capitalized(with: german)
  title.capitalized(with: docLocale)
  title.capitalized(with: otherUser.locale)

Something at the call site needs to imply this is a locale, and there's nothing in `(with: .current)` that does so. This is arguably just a style issue, though: even though the language allows you to say `(with: .current)`, you really ought to say `(with: Locale.current)` to be clearer. Or perhaps the problem is with the name `current`—it ought to be `currentLocale` (even though that's redundant when you write it out as `Locale.currentLocale`), or it should use some location-ish terminology like `home` or `native`.

(Actually, I think there might be a new guideline there: Variable and property names should at least hint at the type of the value they contain. Names like `current` or `shared` or `default` are too vague, and should usually be paired with a word that implies the type.)

It might also help to change the `with` preposition to `in`, which would at least imply that the parameter is related to some kind of location.

  title.capitalized(in: german)
  title.capitalized(in: docLocale)
  title.capitalized(in: otherUser.locale)
  title.capitalized(in: .current) // Still not great, but better

(3) myDictionary.elementRemoved(atIndex:)

This naming is exactly backwards, and is a perfect example of why we *don't* want rigid consistency:

  1. It emphasizes the element being returned, while making the "Removed" operation an afterthought, even though the removal is the main thing you want to happen and the element is returned as an afterthought.

  2. It mentions the "Index", even though there is no other plausible thing that could be labeled "at". (The only other plausible parameters to a `remove` method are an element, an array of elements, a predicate, or a range of indices. Of those four, only the range of indices could possibly make sense with "at", but that ambiguity is a harmless overloading.)

Again, think about a use site:

  func tableView(_: UITableView, commit edit: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    assert(edit == .delete)
    
    sections[indexPath.section].rows.elementRemoved(atIndex: indexPath.row)
    // vs.
    sections[indexPath.section].rows.remove(at: indexPath.row)
  }

In context, `elementRemoved` obscures the purpose of the line, and there is no ambiguity about what `at` means. The current name is *far* better.

(4) myDate.timeInterval(sinceDate:)

I have a hard time thinking of a call site where `sinceDate` would be an improvement over `since`; the preposition already *strongly* implies a temporal aspect to the parameter.

If we remove `Date`, then in isolation I kind of like this change. The problem arrives when you step out of isolation and think about the other methods in its family. It would be strange to have `myDate.timeInterval(since: otherDate)`, but `myDate.timeIntervalSinceNow()`.

One solution would be to add `now`, `referenceDate`, and `unixEpoch` (or `utc1970`) as static properties on `Date`. Then you could have just the one `timeInterval(since:)` method:

    myDate.timeInterval(since: otherDate)
    myDate.timeInterval(since: .now)
    myDate.timeInterval(since: .referenceDate)
    myDate.timeInterval(since: .unixEpoch)

Another solution would be to add a `-` operator that takes two `Date`s and returns a `TimeInterval`, sidestepping the wording issue entirely.

(5) myArray.each(inClosure: )

I don't get this name at all. This operation is completely imperative and side-effectful, but there's no verb? How is it "in" the closure? Why "closure" when you can pass a function, and in fact you probably will *only* see this label if you're passing a function?

I do think there's a problem with `forEach(_:)`—it ought to be `forEach(do:)`. This is much like `DispatchQueue.async(execute:)` or the `withoutActuallyEscaping(_:do:)` function in the standard library. When you pass a function parameter, and the call's primary purpose is to run that parameter, it's often best to attach the verb to that parameter instead of putting it in the base name. Of course, the complication there is that the verb doesn't actually get written if you use trailing closure syntax, but the curlies sort of fill that role. Kind of.

Although I do understand removing "string" from the latter was to reduce redundancy in function/method declarations, we only make one declaration, yet we make many calls. So increasing ambiguity in calls does not seem like a good trade-off for decreased boilerplate in declarations. More often than not it's calls that we're reading, not the declarations—unless of course the call was ambiguous and we had to read the declaration to make sense out of it. So perhaps we might question if increased ambiguity is an overall good thing.

I think you misunderstand the current Guidelines' goals. The Guidelines are not trying to reduce redundancy at declaration sites—they're trying to reduce redundancy at *call sites*. The idea is that, if the variable names for the method's target and parameters imply something about the types they contain, those names along with the prepositions will imply the purpose of each parameter, and therefore the call. The types are just a more formal version of that check.

That's why the very first paragraph of the API Design Guidelines <https://swift.org/documentation/api-design-guidelines/> says:

Clarity at the point of use is your most important goal. Entities such as methods and properties are declared only once but used repeatedly. Design APIs to make those uses clear and concise. When evaluating a design, reading a declaration is seldom sufficient; always examine a use case to make sure it looks clear in context.

So the tradeoff is not between *declaration redundancy* and call site clarity—it is between *call site redundancy* and call site ambiguity. Because their parameters are unlabeled, most languages have severe call site ambiguity problems. Objective-C has a pretty serious call site redundancy problem. Swift's design is trying to hit the baby bear "just right" point.

It is quite possible that, in some areas, we have swung too far back towards ambiguity. But I don't think `elementRemoved(atIndex:)` is going to fix that.

However this removal of explicit contextual cues from the method signature harms readability, since now, the compiler will let people write code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short variable names or NO variable names. However, spend much time on GitHub or in CocoaPods and you will see an increasing number of codebases where that's exactly what they do, especially in closures.

What I think you're missing with this example—and in fact with all of your closure-based examples—is that closures don't exist in isolation; they're in some larger context. (Otherwise, they won't compile.) For instance, the above closure might be in a line like:

  return zip(databases, recordIDs)
    .map { return $0.fetch(withRecordID:$1) }

Read in the context of its line, the meanings of $0 and $1 are fairly clear.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no reason to expect the type of "myRecordID" not to be CKRecordID. (In fact, it's CKRecord.)

Again, in the larger context, this line will end up generating a `[CKRecord]` array instead of a `[CKRecordID]` array, which is probably going to cause a type mismatch once you try to actually use the alleged record IDs. (But as I said earlier, I can see the case for using `fetchRecord(withID:)` or `fetchRecord(with:)` instead of `fetch(withRecordID:)`.)

Ambiguous names can hide bugs in their ambiguity, but verbose names can also hide bugs in the sheer mass of code they generate. The difference is, developers can manage the ambiguity in their code by naming variables well, but they can't manage verbosity if verbose names are imposed on them.

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

Again, the context of the closure would tell you, but part of the problem here is that they held onto an Objective-C preposition which was poorly chosen. If the line were `$0.draw(in:$1)`, you would know that `$1` specified an area of the screen and `$0` was something that could be drawn, which frankly is all you really *need* to know to understand what this line does.

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value pair, array will be a dict, and key will be an index type! This may seem like a ridiculous example, but I have literally seen things like this.

Where have you seen something like this? `array` would have to be passed `inout` for this to work at all.

Nevertheless, how would more verbose names help with this problem? This is every bit as incorrect, and the compiler will still accept it:

  {array, key in
  let number = array.elementRemoved(atIndex:key)
  return number }

Are you thinking that they'll notice that `atIndex` is not `atKey`? There is already a much stronger safeguard against that: `Dictionary.Index` and `Dictionary.Key` are different, totally incompatible types. Every mechanism I can think of to get a `Dictionary.Index` has "index" or "indices" in its name, so you could only make this mistake if you confused dictionary indices with dictionary keys, in which case `atIndex:` would not stand out to you either.

Ultimately, unless the compiler actually understands identifiers, it's just not going to be able to catch mistakes like calling a dictionary an "array", or many of the other problems you describe here. But the type system can and often does flag these kinds of problems pretty close to the source.

Orphaning method signatures by stripping useful return type and argument type information wouldn't be so bad if variables were all named descriptively, but that is a strangely optimistic hope for a language that's as paranoid about safety that it was specifically designed to prevent many categories of common mistakes.

Personally, I think of Swift's approach to safety as similar to Reagan's "trust, but verify". Our naming conventions trust the programmer to write code with clear names; our picky type system verifies that the code is plausible. We don't force the programmer to explain herself to us until we notice that something doesn't seem right.

The bottom line is, a language can't force you to write clear and correct code, but it has many tools it can use to encourage it. Swift chooses not to use the "heavily redundant names" tool because its cost to good code is too high. We instead rely more on other tools, like strong typing, value types, and definite initialization, to encourage high-quality code at a lower cost.

···

On Aug 2, 2017, at 10:49 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

There is a difficult balance to strike here, to be sure. On the one hand,
Jon has written a very thorough analysis of some pros and cons of Swift's
API guidelines and their real-world implementation. On the other hand,
while the analysis is substantive, unfortunately it does not tread new
ground in terms of what has already been discussed on the list. The
trade-offs and alternatives discussed today *were* studied at the time of
the adoption of these guidelines, and as with any decision to do with
naming, not everyone agreed with all the points. Nonetheless, a decision
was made. Moreover, of Jon's five examples, one is actually not a renaming
at all, one renaming was explicitly approved (with its own special rule),
and the remainder are Apple APIs not subject to Swift Evolution. (I agree
that `timeIntervalSince` seems to be contrary to Swift naming guidelines,
but we don't get to name that API as a community.)

I also disagree very strongly that 1.5 years after a proposal is an
appropriate time to revisit a tentpole feature of a previous release. This
was a major undertaking that occupied the attention of all for a very long
time. There is an interval between 1.5 years and ever again for bringing up
past decisions: as idiomatic Swift grows with time, there may certainly
come a time to revise guidelines laid down for Swift 3. But I strenuously
disagree that Swift 5 is an acceptable timeframe for revising this effort.
The most contentious disagreements on this list have been API renaming and
access control modifiers; the latter is now different for each of Swift 2,
3, and 4. These efforts had costs in terms of time and energy (and likely
took an emotional toll too) on participants. It is not at all "weird" to
regard those conversations as a reason not to continue the conversation; in
fact, based on that experience, I believe strongly that we would
*certainly* be better off as a community by not rehashing API names.

···

On Wed, Aug 2, 2017 at 5:48 PM, Mike Sanderson <m@mikesand.com> wrote:

On Wed, Aug 2, 2017 at 1:49 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

...

This topic was reviewed extensively at the beginning of Swift Evolution.
The idea is that type information does not need to be repeated in the
label. Since the locale is of type Locale, “with” is preferred over
“withLocale” because “withLocale: Locale” is redundant. As this has been
long approved by the community, it’s not in scope to roll it back.

Thanks to everyone who contributed to the list to make the Swift 3 API
guidelines possible. I think the Swift 3 API Guidelines were a tremendous
accomplishment, and I appreciate the actual beauty that can result from its
sparse code.

It seems reasonable that 1 1/2 years after the proposal, more than 1 year
after beta release, and after seeing the API Guidelines applied out in the
world (or not) and applying them ourselves (or not), to bring up the
subject again and ask if it can be improved. I think Jon gave some good
examples of weaknesses that can result, and Robert gave some good
suggestions.

Changing existing code is probably out of scope to avoid language
churn--and it definitely would contradict Swift 4's goal of preserving
source stability. The Great Re-Renaming? No thanks. (Thought I think The
Great Renaming unfairly got the blame for later migration issues.) Still,
these messages today were substantive suggestions for how to improve Swift
API naming conventions. I am re-reading the threads from Jan and Feb 2016,
which seems appropriate to inform a discussion that builds on them, but it
would be weird to regard those conversations as a reason to shut down any
mention of the subject ever again. This seems like exactly the kind of
subject that should be revisited, and could form a proposal later in the
fall.

As someone who admires the API guidelines and how they make Swift read
elegantly, I want to make them even better and easier to use. I myself
think that the English language is too ungainly to support rules for a
language as elegant as Swift. So I hope we can continue to have this
conversation.

Brent had a draft proposal to revise the names of collection methods to improve the situation here. There is room for improvement.

It didn't change `remove(at:)`, though. (Well, it might have affected its return value or something, I don't remember.) It mostly addressed the `dropLast()`, `prefix(_:)` etc. calls.

To respond to the original post:

Some of the APIs you cite are not very well named, but I think your diagnosis is incorrect and so your prescription isn't going to help.

The reason for this is, whenever a preposition is used in English, it almost always takes a dyadic form, relating a subject to the preposition's object. The two most common dyadic formats are:

<subject> [<preposition> <object of preposition>]
<The boy> [<with> <the dog>] crossed the street.

[<preposition> < object of preposition>] <subject>
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature were generally respected in method signatures. However, Swift 3's migration of the preposition inside the parentheses also seems to have been accompanied by the stripping away of either the subject, the prepositional object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
(subject "Record" got removed)

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
(subject "String" and prep. object "Locale" both got removed)

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
(subject "element" already missing from both; prep. object "Index" got removed)

The thing is, the subjects and prepositional objects *are* present in all of these—they are the parameters and targets of the calls.

In your own example, you say "In space, no one can hear you scream", not "In location space, group-of-people no one can hear you scream". So why is it a problem that we say "myString, capitalized with myLocale" instead of "myString, string capitalized with locale myLocale"? These are redundancies that we would never tolerate in natural language; I don't see why you think code should be different.

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
(subject "timeInterval" and object "Date" both still present; but oddly, preposition "since" got left outside of the parentheses)

This is actually inaccurate—the parameter to `timeIntervalSince(_:)` is unlabeled, so it's:

    new: myDate.timeIntervalSince(myDate)

(5) Array:

      old: myArray.forEach({ thing in code})
new: myArray.forEach() { thing in //code }
            (preposition “for” is outside of the parentheses)

Yes, because the preposition does not apply to the parameter—it applies to the operation as a whole. I'll have more to say on this in a moment.

The inconsistency between the examples is shown in the bold text of each example, but lets go over why this matters. It matters because any language is easier to learn the more consistently it sticks to its own rules.

This is true, but you aren't just proposing sticking more closely to our existing standards—you're proposing *changing* our standards. And I don't think the changes you propose are an improvement. In fact, I'd say each of these examples is worse:

(1) myCKDatabase.fetchRecord(withRecordID:)

"Fetch record with record ID"? I mean, you could at least remove the `Record` before `ID`. What other ID do you suppose it would be?

I *can* see the case for going back to `fetchRecord` instead of just `fetch`, though. On the other hand, I do think that, if you know it's a database, the most obvious thing for `fetch` to be fetching is a record from that database. It's not a dog—it won't be fetching a stick.

(2) myString.stringCapitalized(withLocale:)

Let's translate this to an actual use site, which is what we care about.

  func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sections[section].title.stringCapitalized(withLocale: .current)
  }

What is `string` contributing here? We already know it's a "title", which sounds a lot like a string. If you asked for a "capitalized" string, what else would you get back if not another string?

The locale parameter is a little more tricky. You're right that `(with: .current)` is vague, but I can imagine plenty of call sites where `with` wouldn't be:

  title.capitalized(with: german)
  title.capitalized(with: docLocale)
  title.capitalized(with: otherUser.locale)

Something at the call site needs to imply this is a locale, and there's nothing in `(with: .current)` that does so. This is arguably just a style issue, though: even though the language allows you to say `(with: .current)`, you really ought to say `(with: Locale.current)` to be clearer. Or perhaps the problem is with the name `current`—it ought to be `currentLocale` (even though that's redundant when you write it out as `Locale.currentLocale`), or it should use some location-ish terminology like `home` or `native`.

(Actually, I think there might be a new guideline there: Variable and property names should at least hint at the type of the value they contain. Names like `current` or `shared` or `default` are too vague, and should usually be paired with a word that implies the type.)

It might also help to change the `with` preposition to `in`, which would at least imply that the parameter is related to some kind of location.

  title.capitalized(in: german)
  title.capitalized(in: docLocale)
  title.capitalized(in: otherUser.locale)
  title.capitalized(in: .current) // Still not great, but better

(3) myDictionary.elementRemoved(atIndex:)

This naming is exactly backwards, and is a perfect example of why we *don't* want rigid consistency:

  1. It emphasizes the element being returned, while making the "Removed" operation an afterthought, even though the removal is the main thing you want to happen and the element is returned as an afterthought.

  2. It mentions the "Index", even though there is no other plausible thing that could be labeled "at". (The only other plausible parameters to a `remove` method are an element, an array of elements, a predicate, or a range of indices. Of those four, only the range of indices could possibly make sense with "at", but that ambiguity is a harmless overloading.)

Again, think about a use site:

  func tableView(_: UITableView, commit edit: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    assert(edit == .delete)
    
    sections[indexPath.section].rows.elementRemoved(atIndex: indexPath.row)
    // vs.
    sections[indexPath.section].rows.remove(at: indexPath.row)
  }

In context, `elementRemoved` obscures the purpose of the line, and there is no ambiguity about what `at` means. The current name is *far* better.

(4) myDate.timeInterval(sinceDate:)

I have a hard time thinking of a call site where `sinceDate` would be an improvement over `since`; the preposition already *strongly* implies a temporal aspect to the parameter.

If we remove `Date`, then in isolation I kind of like this change. The problem arrives when you step out of isolation and think about the other methods in its family. It would be strange to have `myDate.timeInterval(since: otherDate)`, but `myDate.timeIntervalSinceNow()`.

One solution would be to add `now`, `referenceDate`, and `unixEpoch` (or `utc1970`) as static properties on `Date`. Then you could have just the one `timeInterval(since:)` method:

    myDate.timeInterval(since: otherDate)
    myDate.timeInterval(since: .now)
    myDate.timeInterval(since: .referenceDate)
    myDate.timeInterval(since: .unixEpoch)

Another solution would be to add a `-` operator that takes two `Date`s and returns a `TimeInterval`, sidestepping the wording issue entirely.

(5) myArray.each(inClosure: )

I don't get this name at all. This operation is completely imperative and side-effectful, but there's no verb? How is it "in" the closure? Why "closure" when you can pass a function, and in fact you probably will *only* see this label if you're passing a function?

I do think there's a problem with `forEach(_:)`—it ought to be `forEach(do:)`. This is much like `DispatchQueue.async(execute:)` or the `withoutActuallyEscaping(_:do:)` function in the standard library. When you pass a function parameter, and the call's primary purpose is to run that parameter, it's often best to attach the verb to that parameter instead of putting it in the base name. Of course, the complication there is that the verb doesn't actually get written if you use trailing closure syntax, but the curlies sort of fill that role. Kind of.

Although I do understand removing "string" from the latter was to reduce redundancy in function/method declarations, we only make one declaration, yet we make many calls. So increasing ambiguity in calls does not seem like a good trade-off for decreased boilerplate in declarations. More often than not it's calls that we're reading, not the declarations—unless of course the call was ambiguous and we had to read the declaration to make sense out of it. So perhaps we might question if increased ambiguity is an overall good thing.

I think you misunderstand the current Guidelines' goals. The Guidelines are not trying to reduce redundancy at declaration sites—they're trying to reduce redundancy at *call sites*. The idea is that, if the variable names for the method's target and parameters imply something about the types they contain, those names along with the prepositions will imply the purpose of each parameter, and therefore the call. The types are just a more formal version of that check.

That's why the very first paragraph of the API Design Guidelines <https://swift.org/documentation/api-design-guidelines/> says:

Clarity at the point of use is your most important goal. Entities such as methods and properties are declared only once but used repeatedly. Design APIs to make those uses clear and concise. When evaluating a design, reading a declaration is seldom sufficient; always examine a use case to make sure it looks clear in context.

So the tradeoff is not between *declaration redundancy* and call site clarity—it is between *call site redundancy* and call site ambiguity. Because their parameters are unlabeled, most languages have severe call site ambiguity problems. Objective-C has a pretty serious call site redundancy problem. Swift's design is trying to hit the baby bear "just right" point.

It is quite possible that, in some areas, we have swung too far back towards ambiguity. But I don't think `elementRemoved(atIndex:)` is going to fix that.

However this removal of explicit contextual cues from the method signature harms readability, since now, the compiler will let people write code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short variable names or NO variable names. However, spend much time on GitHub or in CocoaPods and you will see an increasing number of codebases where that's exactly what they do, especially in closures.

What I think you're missing with this example—and in fact with all of your closure-based examples—is that closures don't exist in isolation; they're in some larger context. (Otherwise, they won't compile.) For instance, the above closure might be in a line like:

  return zip(databases, recordIDs)
    .map { return $0.fetch(withRecordID:$1) }

Read in the context of its line, the meanings of $0 and $1 are fairly clear.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no reason to expect the type of "myRecordID" not to be CKRecordID. (In fact, it's CKRecord.)

Again, in the larger context, this line will end up generating a `[CKRecord]` array instead of a `[CKRecordID]` array, which is probably going to cause a type mismatch once you try to actually use the alleged record IDs. (But as I said earlier, I can see the case for using `fetchRecord(withID:)` or `fetchRecord(with:)` instead of `fetch(withRecordID:)`.)

Ambiguous names can hide bugs in their ambiguity, but verbose names can also hide bugs in the sheer mass of code they generate. The difference is, developers can manage the ambiguity in their code by naming variables well, but they can't manage verbosity if verbose names are imposed on them.

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

Again, the context of the closure would tell you, but part of the problem here is that they held onto an Objective-C preposition which was poorly chosen. If the line were `$0.draw(in:$1)`, you would know that `$1` specified an area of the screen and `$0` was something that could be drawn, which frankly is all you really *need* to know to understand what this line does.

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value pair, array will be a dict, and key will be an index type! This may seem like a ridiculous example, but I have literally seen things like this.

Where have you seen something like this? `array` would have to be passed `inout` for this to work at all.

Nevertheless, how would more verbose names help with this problem? This is every bit as incorrect, and the compiler will still accept it:

  {array, key in
  let number = array.elementRemoved(atIndex:key)
  return number }

Are you thinking that they'll notice that `atIndex` is not `atKey`? There is already a much stronger safeguard against that: `Dictionary.Index` and `Dictionary.Key` are different, totally incompatible types. Every mechanism I can think of to get a `Dictionary.Index` has "index" or "indices" in its name, so you could only make this mistake if you confused dictionary indices with dictionary keys, in which case `atIndex:` would not stand out to you either.

Ultimately, unless the compiler actually understands identifiers, it's just not going to be able to catch mistakes like calling a dictionary an "array", or many of the other problems you describe here. But the type system can and often does flag these kinds of problems pretty close to the source.

Orphaning method signatures by stripping useful return type and argument type information wouldn't be so bad if variables were all named descriptively, but that is a strangely optimistic hope for a language that's as paranoid about safety that it was specifically designed to prevent many categories of common mistakes.

Personally, I think of Swift's approach to safety as similar to Reagan's "trust, but verify". Our naming conventions trust the programmer to write code with clear names; our picky type system verifies that the code is plausible. We don't force the programmer to explain herself to us until we notice that something doesn't seem right.

The bottom line is, a language can't force you to write clear and correct code, but it has many tools it can use to encourage it. Swift chooses not to use the "heavily redundant names" tool because its cost to good code is too high. We instead rely more on other tools, like strong typing, value types, and definite initialization, to encourage high-quality code at a lower cost.

More verbose rather than heavily redundant names tool, but what have we lost by being reliant on strongish static typing and value types? Would Cocoa/ UIKit be so nice to use if rewritten fully in idiomatic non @objc Swift (compared to working with UI on C++ platforms)? What would it tell us?
Sorry for the rant... do not take static typing laying down j/k ;). (still quite in “love” with the opt-in security that annotations and clang warnings bring in Objective-C rather than the opt-out freedom model some other languages have)

···

Sent from my iPhone

On 3 Aug 2017, at 04:39, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 2, 2017, at 10:49 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

If you want to use Objective-C you're free to use Objective-C, or more generally any of the wonderful languages that choose a different tradeoff regarding convenience over expressiveness. Otherwise, I'm not really sure what productive forward movement bringing up complaints about a fundamental philosophical underpinning of Swift that's not realistically going to change is supposed to achieve. As was mentioned in a message earlier this week, swift-evolution is a list to discuss making changes to the Swift language, not a list for ranting about things in Swift you don't like but cannot change.

Regards,
Austin

···

On Aug 2, 2017, at 11:43 PM, Goffredo Marocchi via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPhone

On 3 Aug 2017, at 04:39, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 2, 2017, at 10:49 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Brent had a draft proposal to revise the names of collection methods to improve the situation here. There is room for improvement.

It didn't change `remove(at:)`, though. (Well, it might have affected its return value or something, I don't remember.) It mostly addressed the `dropLast()`, `prefix(_:)` etc. calls.

To respond to the original post:

Some of the APIs you cite are not very well named, but I think your diagnosis is incorrect and so your prescription isn't going to help.

The reason for this is, whenever a preposition is used in English, it almost always takes a dyadic form, relating a subject to the preposition's object. The two most common dyadic formats are:

<subject> [<preposition> <object of preposition>]
<The boy> [<with> <the dog>] crossed the street.

[<preposition> < object of preposition>] <subject>
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature were generally respected in method signatures. However, Swift 3's migration of the preposition inside the parentheses also seems to have been accompanied by the stripping away of either the subject, the prepositional object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
(subject "Record" got removed)

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
(subject "String" and prep. object "Locale" both got removed)

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
(subject "element" already missing from both; prep. object "Index" got removed)

The thing is, the subjects and prepositional objects *are* present in all of these—they are the parameters and targets of the calls.

In your own example, you say "In space, no one can hear you scream", not "In location space, group-of-people no one can hear you scream". So why is it a problem that we say "myString, capitalized with myLocale" instead of "myString, string capitalized with locale myLocale"? These are redundancies that we would never tolerate in natural language; I don't see why you think code should be different.

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
(subject "timeInterval" and object "Date" both still present; but oddly, preposition "since" got left outside of the parentheses)

This is actually inaccurate—the parameter to `timeIntervalSince(_:)` is unlabeled, so it's:

    new: myDate.timeIntervalSince(myDate)

(5) Array:

      old: myArray.forEach({ thing in code})
new: myArray.forEach() { thing in //code }
            (preposition “for” is outside of the parentheses)

Yes, because the preposition does not apply to the parameter—it applies to the operation as a whole. I'll have more to say on this in a moment.

The inconsistency between the examples is shown in the bold text of each example, but lets go over why this matters. It matters because any language is easier to learn the more consistently it sticks to its own rules.

This is true, but you aren't just proposing sticking more closely to our existing standards—you're proposing *changing* our standards. And I don't think the changes you propose are an improvement. In fact, I'd say each of these examples is worse:

(1) myCKDatabase.fetchRecord(withRecordID:)

"Fetch record with record ID"? I mean, you could at least remove the `Record` before `ID`. What other ID do you suppose it would be?

I *can* see the case for going back to `fetchRecord` instead of just `fetch`, though. On the other hand, I do think that, if you know it's a database, the most obvious thing for `fetch` to be fetching is a record from that database. It's not a dog—it won't be fetching a stick.

(2) myString.stringCapitalized(withLocale:)

Let's translate this to an actual use site, which is what we care about.

  func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sections[section].title.stringCapitalized(withLocale: .current)
  }

What is `string` contributing here? We already know it's a "title", which sounds a lot like a string. If you asked for a "capitalized" string, what else would you get back if not another string?

The locale parameter is a little more tricky. You're right that `(with: .current)` is vague, but I can imagine plenty of call sites where `with` wouldn't be:

  title.capitalized(with: german)
  title.capitalized(with: docLocale)
  title.capitalized(with: otherUser.locale)

Something at the call site needs to imply this is a locale, and there's nothing in `(with: .current)` that does so. This is arguably just a style issue, though: even though the language allows you to say `(with: .current)`, you really ought to say `(with: Locale.current)` to be clearer. Or perhaps the problem is with the name `current`—it ought to be `currentLocale` (even though that's redundant when you write it out as `Locale.currentLocale`), or it should use some location-ish terminology like `home` or `native`.

(Actually, I think there might be a new guideline there: Variable and property names should at least hint at the type of the value they contain. Names like `current` or `shared` or `default` are too vague, and should usually be paired with a word that implies the type.)

It might also help to change the `with` preposition to `in`, which would at least imply that the parameter is related to some kind of location.

  title.capitalized(in: german)
  title.capitalized(in: docLocale)
  title.capitalized(in: otherUser.locale)
  title.capitalized(in: .current) // Still not great, but better

(3) myDictionary.elementRemoved(atIndex:)

This naming is exactly backwards, and is a perfect example of why we *don't* want rigid consistency:

  1. It emphasizes the element being returned, while making the "Removed" operation an afterthought, even though the removal is the main thing you want to happen and the element is returned as an afterthought.

  2. It mentions the "Index", even though there is no other plausible thing that could be labeled "at". (The only other plausible parameters to a `remove` method are an element, an array of elements, a predicate, or a range of indices. Of those four, only the range of indices could possibly make sense with "at", but that ambiguity is a harmless overloading.)

Again, think about a use site:

  func tableView(_: UITableView, commit edit: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    assert(edit == .delete)
    
    sections[indexPath.section].rows.elementRemoved(atIndex: indexPath.row)
    // vs.
    sections[indexPath.section].rows.remove(at: indexPath.row)
  }

In context, `elementRemoved` obscures the purpose of the line, and there is no ambiguity about what `at` means. The current name is *far* better.

(4) myDate.timeInterval(sinceDate:)

I have a hard time thinking of a call site where `sinceDate` would be an improvement over `since`; the preposition already *strongly* implies a temporal aspect to the parameter.

If we remove `Date`, then in isolation I kind of like this change. The problem arrives when you step out of isolation and think about the other methods in its family. It would be strange to have `myDate.timeInterval(since: otherDate)`, but `myDate.timeIntervalSinceNow()`.

One solution would be to add `now`, `referenceDate`, and `unixEpoch` (or `utc1970`) as static properties on `Date`. Then you could have just the one `timeInterval(since:)` method:

    myDate.timeInterval(since: otherDate)
    myDate.timeInterval(since: .now)
    myDate.timeInterval(since: .referenceDate)
    myDate.timeInterval(since: .unixEpoch)

Another solution would be to add a `-` operator that takes two `Date`s and returns a `TimeInterval`, sidestepping the wording issue entirely.

(5) myArray.each(inClosure: )

I don't get this name at all. This operation is completely imperative and side-effectful, but there's no verb? How is it "in" the closure? Why "closure" when you can pass a function, and in fact you probably will *only* see this label if you're passing a function?

I do think there's a problem with `forEach(_:)`—it ought to be `forEach(do:)`. This is much like `DispatchQueue.async(execute:)` or the `withoutActuallyEscaping(_:do:)` function in the standard library. When you pass a function parameter, and the call's primary purpose is to run that parameter, it's often best to attach the verb to that parameter instead of putting it in the base name. Of course, the complication there is that the verb doesn't actually get written if you use trailing closure syntax, but the curlies sort of fill that role. Kind of.

Although I do understand removing "string" from the latter was to reduce redundancy in function/method declarations, we only make one declaration, yet we make many calls. So increasing ambiguity in calls does not seem like a good trade-off for decreased boilerplate in declarations. More often than not it's calls that we're reading, not the declarations—unless of course the call was ambiguous and we had to read the declaration to make sense out of it. So perhaps we might question if increased ambiguity is an overall good thing.

I think you misunderstand the current Guidelines' goals. The Guidelines are not trying to reduce redundancy at declaration sites—they're trying to reduce redundancy at *call sites*. The idea is that, if the variable names for the method's target and parameters imply something about the types they contain, those names along with the prepositions will imply the purpose of each parameter, and therefore the call. The types are just a more formal version of that check.

That's why the very first paragraph of the API Design Guidelines <https://swift.org/documentation/api-design-guidelines/> says:

Clarity at the point of use is your most important goal. Entities such as methods and properties are declared only once but used repeatedly. Design APIs to make those uses clear and concise. When evaluating a design, reading a declaration is seldom sufficient; always examine a use case to make sure it looks clear in context.

So the tradeoff is not between *declaration redundancy* and call site clarity—it is between *call site redundancy* and call site ambiguity. Because their parameters are unlabeled, most languages have severe call site ambiguity problems. Objective-C has a pretty serious call site redundancy problem. Swift's design is trying to hit the baby bear "just right" point.

It is quite possible that, in some areas, we have swung too far back towards ambiguity. But I don't think `elementRemoved(atIndex:)` is going to fix that.

However this removal of explicit contextual cues from the method signature harms readability, since now, the compiler will let people write code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short variable names or NO variable names. However, spend much time on GitHub or in CocoaPods and you will see an increasing number of codebases where that's exactly what they do, especially in closures.

What I think you're missing with this example—and in fact with all of your closure-based examples—is that closures don't exist in isolation; they're in some larger context. (Otherwise, they won't compile.) For instance, the above closure might be in a line like:

  return zip(databases, recordIDs)
    .map { return $0.fetch(withRecordID:$1) }

Read in the context of its line, the meanings of $0 and $1 are fairly clear.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no reason to expect the type of "myRecordID" not to be CKRecordID. (In fact, it's CKRecord.)

Again, in the larger context, this line will end up generating a `[CKRecord]` array instead of a `[CKRecordID]` array, which is probably going to cause a type mismatch once you try to actually use the alleged record IDs. (But as I said earlier, I can see the case for using `fetchRecord(withID:)` or `fetchRecord(with:)` instead of `fetch(withRecordID:)`.)

Ambiguous names can hide bugs in their ambiguity, but verbose names can also hide bugs in the sheer mass of code they generate. The difference is, developers can manage the ambiguity in their code by naming variables well, but they can't manage verbosity if verbose names are imposed on them.

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

Again, the context of the closure would tell you, but part of the problem here is that they held onto an Objective-C preposition which was poorly chosen. If the line were `$0.draw(in:$1)`, you would know that `$1` specified an area of the screen and `$0` was something that could be drawn, which frankly is all you really *need* to know to understand what this line does.

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value pair, array will be a dict, and key will be an index type! This may seem like a ridiculous example, but I have literally seen things like this.

Where have you seen something like this? `array` would have to be passed `inout` for this to work at all.

Nevertheless, how would more verbose names help with this problem? This is every bit as incorrect, and the compiler will still accept it:

  {array, key in
  let number = array.elementRemoved(atIndex:key)
  return number }

Are you thinking that they'll notice that `atIndex` is not `atKey`? There is already a much stronger safeguard against that: `Dictionary.Index` and `Dictionary.Key` are different, totally incompatible types. Every mechanism I can think of to get a `Dictionary.Index` has "index" or "indices" in its name, so you could only make this mistake if you confused dictionary indices with dictionary keys, in which case `atIndex:` would not stand out to you either.

Ultimately, unless the compiler actually understands identifiers, it's just not going to be able to catch mistakes like calling a dictionary an "array", or many of the other problems you describe here. But the type system can and often does flag these kinds of problems pretty close to the source.

Orphaning method signatures by stripping useful return type and argument type information wouldn't be so bad if variables were all named descriptively, but that is a strangely optimistic hope for a language that's as paranoid about safety that it was specifically designed to prevent many categories of common mistakes.

Personally, I think of Swift's approach to safety as similar to Reagan's "trust, but verify". Our naming conventions trust the programmer to write code with clear names; our picky type system verifies that the code is plausible. We don't force the programmer to explain herself to us until we notice that something doesn't seem right.

The bottom line is, a language can't force you to write clear and correct code, but it has many tools it can use to encourage it. Swift chooses not to use the "heavily redundant names" tool because its cost to good code is too high. We instead rely more on other tools, like strong typing, value types, and definite initialization, to encourage high-quality code at a lower cost.

More verbose rather than heavily redundant names tool, but what have we lost by being reliant on strongish static typing and value types? Would Cocoa/ UIKit be so nice to use if rewritten fully in idiomatic non @objc Swift (compared to working with UI on C++ platforms)? What would it tell us?
Sorry for the rant... do not take static typing laying down j/k ;). (still quite in “love” with the opt-in security that annotations and clang warnings bring in Objective-C rather than the opt-out freedom model some other languages have)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Because what is the current idiomatic Swift of today may not be the Swift
of tomorrow if people are allowed to keep shaping it as it gets used in the
real world. Ideas can be imported from other languages and today's ideas
and philosophy are not dogmas.

The problem I see with your email is that it does not contribute anything
to the discussion.

- We _know_ Swift has an expressivity problem compared to Objective-C, many
people have made this point loud and clear.
- There are no useful concrete proposals or suggestions of design
directions expressed in this email besides the rehashing of a point that
has been made ad nauseaum on these lists, especially in places more
appropriate for it (e.g. in proposal reviews that involved strengthening
the type system).

Swift does value expressiveness, see the use of argument labels which is
perhaps more pervasive than it was in Objective-C (even more before the
change in argument labels that hit closures and callbacks).

Here is the crux of my disagreement with your email.

Method naming conventions and static typing can be adjusted nearly
independently of one another, and bringing up the latter in a discussion
about the former is _not helpful_.

You could remove Swift's type system tomorrow and still improve how methods
are named. In fact, Swift's current naming conventions are quite suited for
a dynamic language. You could also strengthen Swift's type system and
improve method naming conventions. The two do not march in lockstep.
"Expressiveness" is a huge, vague, and wide-reaching topic. Claiming that
static typing is an expressiveness concern and therefore that complaining
about it in a thread that deals with an almost completely unrelated feature
strikes me as disingenuous. Help me out here.

I do not want to add noise to useful discussions so please do not assume
the worst in my statements, but I also do not think that the idea of "if
you do not like <a>, go back to your <insert expletive> country" has merit
or is helpful to the evolution of the language.

Please.

The issue at hand is not "go back to your country", it is "use the right
tool for the job at hand".

Engineering, like every field of human endeavor, has political elements,
but it is not itself a fundamentally political endeavor. This gross
anthropomorphization is beneath both you and I, and is better avoided for
the happiness of all.

As for contributing to the evolution of the language, your post is not the
first of its quality, specific form, and sentiment that I've seen on the
list, but only the first that I've been moved to respond to. So if I can
steer future discussion in a more productive direction, I will consider
this email chain to be a net positive, even if I have to send out quite a
few more of these emails in reply :).

With the utmost respect,
Austin

···

On Thu, Aug 3, 2017 at 8:13 AM, Goffredo Marocchi <panajev@gmail.com> wrote:

On Thu, Aug 3, 2017 at 3:16 PM, Austin Zheng <austinzheng@gmail.com> > wrote:

If you want to use Objective-C you're free to use Objective-C, or more
generally any of the wonderful languages that choose a different tradeoff
regarding convenience over expressiveness. Otherwise, I'm not really sure
what productive forward movement bringing up complaints about a fundamental
philosophical underpinning of Swift that's not realistically going to
change is supposed to achieve. As was mentioned in a message earlier this
week, swift-evolution is a list to discuss making changes to the Swift
language, not a list for ranting about things in Swift you don't like but
cannot change.

Regards,
Austin

On Aug 2, 2017, at 11:43 PM, Goffredo Marocchi via swift-evolution < >> swift-evolution@swift.org> wrote:

Sent from my iPhone

On 3 Aug 2017, at 04:39, Brent Royal-Gordon via swift-evolution < >> swift-evolution@swift.org> wrote:

On Aug 2, 2017, at 10:49 AM, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

Brent had a draft proposal to revise the names of collection methods to
improve the situation here. There is room for improvement.

It didn't change `remove(at:)`, though. (Well, it might have affected its
return value or something, I don't remember.) It mostly addressed the
`dropLast()`, `prefix(_:)` etc. calls.

To respond to the original post:

Some of the APIs you cite are not very well named, but I think your
diagnosis is incorrect and so your prescription isn't going to help.

The reason for this is, whenever a preposition is used in English, it
almost always takes a dyadic form, relating a subject to the preposition's
object. The two most common dyadic formats are:

*<subject> [<preposition> <object of preposition>]*
<The boy> [<with> <the dog>] crossed the street.

*[<preposition> <** object of preposition**>] <subject>*
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature
were generally respected in method signatures. However, Swift 3's migration
of the preposition inside the parentheses also seems to have been
accompanied by the stripping away of either the subject, the prepositional
object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
*(subject "Record" got removed)*

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
*(subject "String" and prep. object "Locale" both got removed)*

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
*(subject "element" already missing from both; prep. object "Index" got
removed)*

The thing is, the subjects and prepositional objects *are* present in all
of these—they are the parameters and targets of the calls.

In your own example, you say "In space, no one can hear you scream", not
"In location space, group-of-people no one can hear you scream". So why is
it a problem that we say "myString, capitalized with myLocale" instead of
"myString, string capitalized with locale myLocale"? These are redundancies
that we would never tolerate in natural language; I don't see why you think
code should be different.

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
*(subject "timeInterval" and object "Date" both still present; but oddly,
preposition "since" got left outside of the parentheses)*

This is actually inaccurate—the parameter to `timeIntervalSince(_:)` is
unlabeled, so it's:

new: myDate.timeIntervalSince(myDate)

(5) Array:

    old: myArray.forEach({ thing in code})

new: myArray.forEach() { thing in //code }

            *(preposition “for” is outside of the parentheses)*

Yes, because the preposition does not apply to the parameter—it applies
to the operation as a whole. I'll have more to say on this in a moment.

The inconsistency between the examples is shown in the bold text of each
example, but lets go over why this matters. It matters because any language
is easier to learn the more consistently it sticks to its own rules.

This is true, but you aren't just proposing sticking more closely to our
existing standards—you're proposing *changing* our standards. And I don't
think the changes you propose are an improvement. In fact, I'd say each of
these examples is worse:

(1) myCKDatabase.fetchRecord(withRecordID:)

"Fetch record with record ID"? I mean, you could at least remove the
`Record` before `ID`. What other ID do you suppose it would be?

I *can* see the case for going back to `fetchRecord` instead of just
`fetch`, though. On the other hand, I do think that, if you know it's a
database, the most obvious thing for `fetch` to be fetching is a record
from that database. It's not a dog—it won't be fetching a stick.

(2) myString.stringCapitalized(withLocale:)

Let's translate this to an actual use site, which is what we care about.

func tableView(_: UITableView, titleForHeaderInSection section: Int) ->
String? {
return sections[section].title.stringCapitalized(withLocale: .current)
}

What is `string` contributing here? We already know it's a "title", which
sounds a lot like a string. If you asked for a "capitalized" string, what
else would you get back if not another string?

The locale parameter is a little more tricky. You're right that `(with:
.current)` is vague, but I can imagine plenty of call sites where `with`
wouldn't be:

title.capitalized(with: german)
title.capitalized(with: docLocale)
title.capitalized(with: otherUser.locale)

Something at the call site needs to imply this is a locale, and there's
nothing in `(with: .current)` that does so. This is arguably just a style
issue, though: even though the language allows you to say `(with:
.current)`, you really ought to say `(with: Locale.current)` to be clearer.
Or perhaps the problem is with the name `current`—it ought to be
`currentLocale` (even though that's redundant when you write it out as
`Locale.currentLocale`), or it should use some location-ish terminology
like `home` or `native`.

(Actually, I think there might be a new guideline there: Variable and
property names should at least hint at the type of the value they contain.
Names like `current` or `shared` or `default` are too vague, and should
usually be paired with a word that implies the type.)

It might also help to change the `with` preposition to `in`, which would
at least imply that the parameter is related to some kind of location.

title.capitalized(in: german)
title.capitalized(in: docLocale)
title.capitalized(in: otherUser.locale)
title.capitalized(in: .current) // Still not great, but better

(3) myDictionary.elementRemoved(atIndex:)

This naming is exactly backwards, and is a perfect example of why we
*don't* want rigid consistency:

1. It emphasizes the element being returned, while making the "Removed"
operation an afterthought, even though the removal is the main thing you
want to happen and the element is returned as an afterthought.

2. It mentions the "Index", even though there is no other plausible thing
that could be labeled "at". (The only other plausible parameters to a
`remove` method are an element, an array of elements, a predicate, or a
range of indices. Of those four, only the range of indices could possibly
make sense with "at", but that ambiguity is a harmless overloading.)

Again, think about a use site:

func tableView(_: UITableView, commit edit: UITableViewCellEditingStyle,
forRowAt indexPath: IndexPath) {
assert(edit == .delete)
sections[indexPath.section].rows.elementRemoved(atIndex: indexPath.row)
// vs.
sections[indexPath.section].rows.remove(at: indexPath.row)
}

In context, `elementRemoved` obscures the purpose of the line, and there
is no ambiguity about what `at` means. The current name is *far* better.

(4) myDate.timeInterval(sinceDate:)

I have a hard time thinking of a call site where `sinceDate` would be an
improvement over `since`; the preposition already *strongly* implies a
temporal aspect to the parameter.

If we remove `Date`, then in isolation I kind of like this change. The
problem arrives when you step out of isolation and think about the other
methods in its family. It would be strange to have
`myDate.timeInterval(since: otherDate)`, but `myDate.timeIntervalSinceNow()
`.

One solution would be to add `now`, `referenceDate`, and `unixEpoch` (or
`utc1970`) as static properties on `Date`. Then you could have just the one
`timeInterval(since:)` method:

myDate.timeInterval(since: otherDate)
myDate.timeInterval(since: .now)
myDate.timeInterval(since: .referenceDate)
myDate.timeInterval(since: .unixEpoch)

Another solution would be to add a `-` operator that takes two `Date`s
and returns a `TimeInterval`, sidestepping the wording issue entirely.

(5) myArray.each(inClosure: )

I don't get this name at all. This operation is completely imperative and
side-effectful, but there's no verb? How is it "in" the closure? Why
"closure" when you can pass a function, and in fact you probably will
*only* see this label if you're passing a function?

I do think there's a problem with `forEach(_:)`—it ought to be
`forEach(do:)`. This is much like `DispatchQueue.async(execute:)` or the
`withoutActuallyEscaping(_:do:)` function in the standard library. When
you pass a function parameter, and the call's primary purpose is to run
that parameter, it's often best to attach the verb to that parameter
instead of putting it in the base name. Of course, the complication there
is that the verb doesn't actually get written if you use trailing closure
syntax, but the curlies sort of fill that role. Kind of.

Although I do understand removing "string" from the latter was to reduce
redundancy in function/method declarations, we only make one declaration,
yet we make many calls. So increasing ambiguity in calls does not seem like
a good trade-off for decreased boilerplate in declarations. More often than
not it's calls that we're reading, not the declarations—unless of course
the call was ambiguous and we had to read the declaration to make sense out
of it. So perhaps we might question if increased ambiguity is an overall
good thing.

I think you misunderstand the current Guidelines' goals. The Guidelines
are not trying to reduce redundancy at declaration sites—they're trying to
reduce redundancy at *call sites*. The idea is that, if the variable names
for the method's target and parameters imply something about the types they
contain, those names along with the prepositions will imply the purpose of
each parameter, and therefore the call. The types are just a more formal
version of that check.

That's why the very first paragraph of the API Design Guidelines <
https://swift.org/documentation/api-design-guidelines/> says:

Clarity at the point of use is your most important goal. Entities such as
methods and properties are declared only once but used repeatedly. Design
APIs to make those uses clear and concise. When evaluating a design,
reading a declaration is seldom sufficient; always examine a use case to
make sure it looks clear in context.

So the tradeoff is not between *declaration redundancy* and call site
clarity—it is between *call site redundancy* and call site ambiguity.
Because their parameters are unlabeled, most languages have severe call
site ambiguity problems. Objective-C has a pretty serious call site
redundancy problem. Swift's design is trying to hit the baby bear "just
right" point.

It is quite possible that, in some areas, we have swung too far back
towards ambiguity. But I don't think `elementRemoved(atIndex:)` is going to
fix that.

However this removal of explicit contextual cues from the method
signature harms readability, since now, the compiler will let people write
code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short
variable names or NO variable names. However, spend much time on GitHub or
in CocoaPods and you will see an increasing number of codebases where
that's exactly what they do, especially in closures.

What I think you're missing with this example—and in fact with all of
your closure-based examples—is that closures don't exist in isolation;
they're in some larger context. (Otherwise, they won't compile.) For
instance, the above closure might be in a line like:

return zip(databases, recordIDs)
.map { return $0.fetch(withRecordID:$1) }

Read in the context of its line, the meanings of $0 and $1 are fairly
clear.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no
reason to expect the type of "myRecordID" not to be CKRecordID. (In fact,
it's CKRecord.)

Again, in the larger context, this line will end up generating a
`[CKRecord]` array instead of a `[CKRecordID]` array, which is probably
going to cause a type mismatch once you try to actually use the alleged
record IDs. (But as I said earlier, I can see the case for using
`fetchRecord(withID:)` or `fetchRecord(with:)` instead of
`fetch(withRecordID:)`.)

Ambiguous names can hide bugs in their ambiguity, but verbose names can
also hide bugs in the sheer mass of code they generate. The difference is,
developers can manage the ambiguity in their code by naming variables well,
but they can't manage verbosity if verbose names are imposed on them.

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

Again, the context of the closure would tell you, but part of the problem
here is that they held onto an Objective-C preposition which was poorly
chosen. If the line were `$0.draw(in:$1)`, you would know that `$1`
specified an area of the screen and `$0` was something that could be drawn,
which frankly is all you really *need* to know to understand what this line
does.

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value
pair, array will be a dict, and key will be an index type! This may seem
like a ridiculous example, but I have literally seen things like this.

Where have you seen something like this? `array` would have to be passed
`inout` for this to work at all.

Nevertheless, how would more verbose names help with this problem? This
is every bit as incorrect, and the compiler will still accept it:

{array, key in
let number = array.elementRemoved(atIndex:key)
return number }

Are you thinking that they'll notice that `atIndex` is not `atKey`? There
is already a much stronger safeguard against that: `Dictionary.Index` and
`Dictionary.Key` are different, totally incompatible types. Every mechanism
I can think of to get a `Dictionary.Index` has "index" or "indices" in its
name, so you could only make this mistake if you confused dictionary
indices with dictionary keys, in which case `atIndex:` would not stand out
to you either.

Ultimately, unless the compiler actually understands identifiers, it's
just not going to be able to catch mistakes like calling a dictionary an
"array", or many of the other problems you describe here. But the type
system can and often does flag these kinds of problems pretty close to the
source.

Orphaning method signatures by stripping useful return type and argument
type information wouldn't be so bad if variables were all named
descriptively, but that is a strangely optimistic hope for a language
that's as paranoid about safety that it was specifically designed to
prevent many categories of common mistakes.

Personally, I think of Swift's approach to safety as similar to Reagan's
"trust, but verify". Our naming conventions trust the programmer to write
code with clear names; our picky type system verifies that the code is
plausible. We don't force the programmer to explain herself to us until we
notice that something doesn't seem right.

The bottom line is, a language can't force you to write clear and correct
code, but it has many tools it can use to encourage it. Swift chooses not
to use the "heavily redundant names" tool because its cost to good code is
too high. We instead rely more on other tools, like strong typing, value
types, and definite initialization, to encourage high-quality code at a
lower cost.

More verbose rather than heavily redundant names tool, but what have we
lost by being reliant on strongish static typing and value types? Would
Cocoa/ UIKit be so nice to use if rewritten fully in idiomatic non @objc
Swift (compared to working with UI on C++ platforms)? What would it tell us?
Sorry for the rant... do not take static typing laying down j/k ;).
(still quite in “love” with the opt-in security that annotations and clang
warnings bring in Objective-C rather than the opt-out freedom model some
other languages have)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Because what is the current idiomatic Swift of today may not be the Swift
of tomorrow if people are allowed to keep shaping it as it gets used in the
real world. Ideas can be imported from other languages and today's ideas
and philosophy are not dogmas.
Swift does value expressiveness, see the use of argument labels which is
perhaps more pervasive than it was in Objective-C (even more before the
change in argument labels that hit closures and callbacks).

I do not want to add noise to useful discussions so please do not assume
the worst in my statements, but I also do not think that the idea of "if
you do not like <a>, go back to your <insert expletive> country" has merit
or is helpful to the evolution of the language.

···

---------- Forwarded message ----------
From: Goffredo Marocchi <panajev@gmail.com>
Date: Thu, Aug 3, 2017 at 4:13 PM
Subject: Re: [swift-evolution] Inconsistencies related to prepositions
To: Austin Zheng <austinzheng@gmail.com>

On Thu, Aug 3, 2017 at 3:16 PM, Austin Zheng <austinzheng@gmail.com> wrote:

If you want to use Objective-C you're free to use Objective-C, or more
generally any of the wonderful languages that choose a different tradeoff
regarding convenience over expressiveness. Otherwise, I'm not really sure
what productive forward movement bringing up complaints about a fundamental
philosophical underpinning of Swift that's not realistically going to
change is supposed to achieve. As was mentioned in a message earlier this
week, swift-evolution is a list to discuss making changes to the Swift
language, not a list for ranting about things in Swift you don't like but
cannot change.

Regards,
Austin

On Aug 2, 2017, at 11:43 PM, Goffredo Marocchi via swift-evolution < > swift-evolution@swift.org> wrote:

Sent from my iPhone

On 3 Aug 2017, at 04:39, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:

On Aug 2, 2017, at 10:49 AM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

Brent had a draft proposal to revise the names of collection methods to
improve the situation here. There is room for improvement.

It didn't change `remove(at:)`, though. (Well, it might have affected its
return value or something, I don't remember.) It mostly addressed the
`dropLast()`, `prefix(_:)` etc. calls.

To respond to the original post:

Some of the APIs you cite are not very well named, but I think your
diagnosis is incorrect and so your prescription isn't going to help.

The reason for this is, whenever a preposition is used in English, it
almost always takes a dyadic form, relating a subject to the preposition's
object. The two most common dyadic formats are:

*<subject> [<preposition> <object of preposition>]*
<The boy> [<with> <the dog>] crossed the street.

*[<preposition> <** object of preposition**>] <subject>*
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature
were generally respected in method signatures. However, Swift 3's migration
of the preposition inside the parentheses also seems to have been
accompanied by the stripping away of either the subject, the prepositional
object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
*(subject "Record" got removed)*

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
*(subject "String" and prep. object "Locale" both got removed)*

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
*(subject "element" already missing from both; prep. object "Index" got
removed)*

The thing is, the subjects and prepositional objects *are* present in all
of these—they are the parameters and targets of the calls.

In your own example, you say "In space, no one can hear you scream", not
"In location space, group-of-people no one can hear you scream". So why is
it a problem that we say "myString, capitalized with myLocale" instead of
"myString, string capitalized with locale myLocale"? These are redundancies
that we would never tolerate in natural language; I don't see why you think
code should be different.

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
*(subject "timeInterval" and object "Date" both still present; but oddly,
preposition "since" got left outside of the parentheses)*

This is actually inaccurate—the parameter to `timeIntervalSince(_:)` is
unlabeled, so it's:

new: myDate.timeIntervalSince(myDate)

(5) Array:

    old: myArray.forEach({ thing in code})

new: myArray.forEach() { thing in //code }

            *(preposition “for” is outside of the parentheses)*

Yes, because the preposition does not apply to the parameter—it applies to
the operation as a whole. I'll have more to say on this in a moment.

The inconsistency between the examples is shown in the bold text of each
example, but lets go over why this matters. It matters because any language
is easier to learn the more consistently it sticks to its own rules.

This is true, but you aren't just proposing sticking more closely to our
existing standards—you're proposing *changing* our standards. And I don't
think the changes you propose are an improvement. In fact, I'd say each of
these examples is worse:

(1) myCKDatabase.fetchRecord(withRecordID:)

"Fetch record with record ID"? I mean, you could at least remove the
`Record` before `ID`. What other ID do you suppose it would be?

I *can* see the case for going back to `fetchRecord` instead of just
`fetch`, though. On the other hand, I do think that, if you know it's a
database, the most obvious thing for `fetch` to be fetching is a record
from that database. It's not a dog—it won't be fetching a stick.

(2) myString.stringCapitalized(withLocale:)

Let's translate this to an actual use site, which is what we care about.

func tableView(_: UITableView, titleForHeaderInSection section: Int) ->
String? {
return sections[section].title.stringCapitalized(withLocale: .current)
}

What is `string` contributing here? We already know it's a "title", which
sounds a lot like a string. If you asked for a "capitalized" string, what
else would you get back if not another string?

The locale parameter is a little more tricky. You're right that `(with:
.current)` is vague, but I can imagine plenty of call sites where `with`
wouldn't be:

title.capitalized(with: german)
title.capitalized(with: docLocale)
title.capitalized(with: otherUser.locale)

Something at the call site needs to imply this is a locale, and there's
nothing in `(with: .current)` that does so. This is arguably just a style
issue, though: even though the language allows you to say `(with:
.current)`, you really ought to say `(with: Locale.current)` to be clearer.
Or perhaps the problem is with the name `current`—it ought to be
`currentLocale` (even though that's redundant when you write it out as
`Locale.currentLocale`), or it should use some location-ish terminology
like `home` or `native`.

(Actually, I think there might be a new guideline there: Variable and
property names should at least hint at the type of the value they contain.
Names like `current` or `shared` or `default` are too vague, and should
usually be paired with a word that implies the type.)

It might also help to change the `with` preposition to `in`, which would
at least imply that the parameter is related to some kind of location.

title.capitalized(in: german)
title.capitalized(in: docLocale)
title.capitalized(in: otherUser.locale)
title.capitalized(in: .current) // Still not great, but better

(3) myDictionary.elementRemoved(atIndex:)

This naming is exactly backwards, and is a perfect example of why we
*don't* want rigid consistency:

1. It emphasizes the element being returned, while making the "Removed"
operation an afterthought, even though the removal is the main thing you
want to happen and the element is returned as an afterthought.

2. It mentions the "Index", even though there is no other plausible thing
that could be labeled "at". (The only other plausible parameters to a
`remove` method are an element, an array of elements, a predicate, or a
range of indices. Of those four, only the range of indices could possibly
make sense with "at", but that ambiguity is a harmless overloading.)

Again, think about a use site:

func tableView(_: UITableView, commit edit: UITableViewCellEditingStyle,
forRowAt indexPath: IndexPath) {
assert(edit == .delete)
sections[indexPath.section].rows.elementRemoved(atIndex: indexPath.row)
// vs.
sections[indexPath.section].rows.remove(at: indexPath.row)
}

In context, `elementRemoved` obscures the purpose of the line, and there
is no ambiguity about what `at` means. The current name is *far* better.

(4) myDate.timeInterval(sinceDate:)

I have a hard time thinking of a call site where `sinceDate` would be an
improvement over `since`; the preposition already *strongly* implies a
temporal aspect to the parameter.

If we remove `Date`, then in isolation I kind of like this change. The
problem arrives when you step out of isolation and think about the other
methods in its family. It would be strange to have
`myDate.timeInterval(since: otherDate)`, but `myDate.timeIntervalSinceNow()
`.

One solution would be to add `now`, `referenceDate`, and `unixEpoch` (or
`utc1970`) as static properties on `Date`. Then you could have just the one
`timeInterval(since:)` method:

myDate.timeInterval(since: otherDate)
myDate.timeInterval(since: .now)
myDate.timeInterval(since: .referenceDate)
myDate.timeInterval(since: .unixEpoch)

Another solution would be to add a `-` operator that takes two `Date`s and
returns a `TimeInterval`, sidestepping the wording issue entirely.

(5) myArray.each(inClosure: )

I don't get this name at all. This operation is completely imperative and
side-effectful, but there's no verb? How is it "in" the closure? Why
"closure" when you can pass a function, and in fact you probably will
*only* see this label if you're passing a function?

I do think there's a problem with `forEach(_:)`—it ought to be
`forEach(do:)`. This is much like `DispatchQueue.async(execute:)` or the
`withoutActuallyEscaping(_:do:)` function in the standard library. When
you pass a function parameter, and the call's primary purpose is to run
that parameter, it's often best to attach the verb to that parameter
instead of putting it in the base name. Of course, the complication there
is that the verb doesn't actually get written if you use trailing closure
syntax, but the curlies sort of fill that role. Kind of.

Although I do understand removing "string" from the latter was to reduce
redundancy in function/method declarations, we only make one declaration,
yet we make many calls. So increasing ambiguity in calls does not seem like
a good trade-off for decreased boilerplate in declarations. More often than
not it's calls that we're reading, not the declarations—unless of course
the call was ambiguous and we had to read the declaration to make sense out
of it. So perhaps we might question if increased ambiguity is an overall
good thing.

I think you misunderstand the current Guidelines' goals. The Guidelines
are not trying to reduce redundancy at declaration sites—they're trying to
reduce redundancy at *call sites*. The idea is that, if the variable names
for the method's target and parameters imply something about the types they
contain, those names along with the prepositions will imply the purpose of
each parameter, and therefore the call. The types are just a more formal
version of that check.

That's why the very first paragraph of the API Design Guidelines <
https://swift.org/documentation/api-design-guidelines/> says:

Clarity at the point of use is your most important goal. Entities such as
methods and properties are declared only once but used repeatedly. Design
APIs to make those uses clear and concise. When evaluating a design,
reading a declaration is seldom sufficient; always examine a use case to
make sure it looks clear in context.

So the tradeoff is not between *declaration redundancy* and call site
clarity—it is between *call site redundancy* and call site ambiguity.
Because their parameters are unlabeled, most languages have severe call
site ambiguity problems. Objective-C has a pretty serious call site
redundancy problem. Swift's design is trying to hit the baby bear "just
right" point.

It is quite possible that, in some areas, we have swung too far back
towards ambiguity. But I don't think `elementRemoved(atIndex:)` is going to
fix that.

However this removal of explicit contextual cues from the method signature
harms readability, since now, the compiler will let people write code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short
variable names or NO variable names. However, spend much time on GitHub or
in CocoaPods and you will see an increasing number of codebases where
that's exactly what they do, especially in closures.

What I think you're missing with this example—and in fact with all of your
closure-based examples—is that closures don't exist in isolation; they're
in some larger context. (Otherwise, they won't compile.) For instance, the
above closure might be in a line like:

return zip(databases, recordIDs)
.map { return $0.fetch(withRecordID:$1) }

Read in the context of its line, the meanings of $0 and $1 are fairly
clear.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have no
reason to expect the type of "myRecordID" not to be CKRecordID. (In fact,
it's CKRecord.)

Again, in the larger context, this line will end up generating a
`[CKRecord]` array instead of a `[CKRecordID]` array, which is probably
going to cause a type mismatch once you try to actually use the alleged
record IDs. (But as I said earlier, I can see the case for using
`fetchRecord(withID:)` or `fetchRecord(with:)` instead of
`fetch(withRecordID:)`.)

Ambiguous names can hide bugs in their ambiguity, but verbose names can
also hide bugs in the sheer mass of code they generate. The difference is,
developers can manage the ambiguity in their code by naming variables well,
but they can't manage verbosity if verbose names are imposed on them.

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

Again, the context of the closure would tell you, but part of the problem
here is that they held onto an Objective-C preposition which was poorly
chosen. If the line were `$0.draw(in:$1)`, you would know that `$1`
specified an area of the screen and `$0` was something that could be drawn,
which frankly is all you really *need* to know to understand what this line
does.

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value
pair, array will be a dict, and key will be an index type! This may seem
like a ridiculous example, but I have literally seen things like this.

Where have you seen something like this? `array` would have to be passed
`inout` for this to work at all.

Nevertheless, how would more verbose names help with this problem? This is
every bit as incorrect, and the compiler will still accept it:

{array, key in
let number = array.elementRemoved(atIndex:key)
return number }

Are you thinking that they'll notice that `atIndex` is not `atKey`? There
is already a much stronger safeguard against that: `Dictionary.Index` and
`Dictionary.Key` are different, totally incompatible types. Every mechanism
I can think of to get a `Dictionary.Index` has "index" or "indices" in its
name, so you could only make this mistake if you confused dictionary
indices with dictionary keys, in which case `atIndex:` would not stand out
to you either.

Ultimately, unless the compiler actually understands identifiers, it's
just not going to be able to catch mistakes like calling a dictionary an
"array", or many of the other problems you describe here. But the type
system can and often does flag these kinds of problems pretty close to the
source.

Orphaning method signatures by stripping useful return type and argument
type information wouldn't be so bad if variables were all named
descriptively, but that is a strangely optimistic hope for a language
that's as paranoid about safety that it was specifically designed to
prevent many categories of common mistakes.

Personally, I think of Swift's approach to safety as similar to Reagan's
"trust, but verify". Our naming conventions trust the programmer to write
code with clear names; our picky type system verifies that the code is
plausible. We don't force the programmer to explain herself to us until we
notice that something doesn't seem right.

The bottom line is, a language can't force you to write clear and correct
code, but it has many tools it can use to encourage it. Swift chooses not
to use the "heavily redundant names" tool because its cost to good code is
too high. We instead rely more on other tools, like strong typing, value
types, and definite initialization, to encourage high-quality code at a
lower cost.

More verbose rather than heavily redundant names tool, but what have we
lost by being reliant on strongish static typing and value types? Would
Cocoa/ UIKit be so nice to use if rewritten fully in idiomatic non @objc
Swift (compared to working with UI on C++ platforms)? What would it tell us?
Sorry for the rant... do not take static typing laying down j/k ;). (still
quite in “love” with the opt-in security that annotations and clang
warnings bring in Objective-C rather than the opt-out freedom model some
other languages have)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Thank you for the long reply, I do agree that I could have been more
constructive and what I said could have been more on point with the thread
at hand and better argued. I do like a complete and well written rebuttal,
chance for me to learn from it :).

···

On Thu, Aug 3, 2017 at 5:14 PM, Austin Zheng <austinzheng@gmail.com> wrote:

On Thu, Aug 3, 2017 at 8:13 AM, Goffredo Marocchi <panajev@gmail.com> > wrote:

Because what is the current idiomatic Swift of today may not be the Swift
of tomorrow if people are allowed to keep shaping it as it gets used in the
real world. Ideas can be imported from other languages and today's ideas
and philosophy are not dogmas.

The problem I see with your email is that it does not contribute anything
to the discussion.

- We _know_ Swift has an expressivity problem compared to Objective-C,
many people have made this point loud and clear.
- There are no useful concrete proposals or suggestions of design
directions expressed in this email besides the rehashing of a point that
has been made ad nauseaum on these lists, especially in places more
appropriate for it (e.g. in proposal reviews that involved strengthening
the type system).

Swift does value expressiveness, see the use of argument labels which is
perhaps more pervasive than it was in Objective-C (even more before the
change in argument labels that hit closures and callbacks).

Here is the crux of my disagreement with your email.

Method naming conventions and static typing can be adjusted nearly
independently of one another, and bringing up the latter in a discussion
about the former is _not helpful_.

You could remove Swift's type system tomorrow and still improve how
methods are named. In fact, Swift's current naming conventions are quite
suited for a dynamic language. You could also strengthen Swift's type
system and improve method naming conventions. The two do not march in
lockstep. "Expressiveness" is a huge, vague, and wide-reaching topic.
Claiming that static typing is an expressiveness concern and therefore that
complaining about it in a thread that deals with an almost completely
unrelated feature strikes me as disingenuous. Help me out here.

I do not want to add noise to useful discussions so please do not assume
the worst in my statements, but I also do not think that the idea of "if
you do not like <a>, go back to your <insert expletive> country" has merit
or is helpful to the evolution of the language.

Please.

The issue at hand is not "go back to your country", it is "use the right
tool for the job at hand".

Engineering, like every field of human endeavor, has political elements,
but it is not itself a fundamentally political endeavor. This gross
anthropomorphization is beneath both you and I, and is better avoided for
the happiness of all.

As for contributing to the evolution of the language, your post is not the
first of its quality, specific form, and sentiment that I've seen on the
list, but only the first that I've been moved to respond to. So if I can
steer future discussion in a more productive direction, I will consider
this email chain to be a net positive, even if I have to send out quite a
few more of these emails in reply :).

With the utmost respect,
Austin

On Thu, Aug 3, 2017 at 3:16 PM, Austin Zheng <austinzheng@gmail.com> >> wrote:

If you want to use Objective-C you're free to use Objective-C, or more
generally any of the wonderful languages that choose a different tradeoff
regarding convenience over expressiveness. Otherwise, I'm not really sure
what productive forward movement bringing up complaints about a fundamental
philosophical underpinning of Swift that's not realistically going to
change is supposed to achieve. As was mentioned in a message earlier this
week, swift-evolution is a list to discuss making changes to the Swift
language, not a list for ranting about things in Swift you don't like but
cannot change.

Regards,
Austin

On Aug 2, 2017, at 11:43 PM, Goffredo Marocchi via swift-evolution < >>> swift-evolution@swift.org> wrote:

Sent from my iPhone

On 3 Aug 2017, at 04:39, Brent Royal-Gordon via swift-evolution < >>> swift-evolution@swift.org> wrote:

On Aug 2, 2017, at 10:49 AM, Xiaodi Wu via swift-evolution < >>> swift-evolution@swift.org> wrote:

Brent had a draft proposal to revise the names of collection methods to
improve the situation here. There is room for improvement.

It didn't change `remove(at:)`, though. (Well, it might have affected
its return value or something, I don't remember.) It mostly addressed the
`dropLast()`, `prefix(_:)` etc. calls.

To respond to the original post:

Some of the APIs you cite are not very well named, but I think your
diagnosis is incorrect and so your prescription isn't going to help.

The reason for this is, whenever a preposition is used in English, it
almost always takes a dyadic form, relating a subject to the preposition's
object. The two most common dyadic formats are:

*<subject> [<preposition> <object of preposition>]*
<The boy> [<with> <the dog>] crossed the street.

*[<preposition> <** object of preposition**>] <subject>*
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature
were generally respected in method signatures. However, Swift 3's migration
of the preposition inside the parentheses also seems to have been
accompanied by the stripping away of either the subject, the prepositional
object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
*(subject "Record" got removed)*

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
*(subject "String" and prep. object "Locale" both got removed)*

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
*(subject "element" already missing from both; prep. object "Index" got
removed)*

The thing is, the subjects and prepositional objects *are* present in
all of these—they are the parameters and targets of the calls.

In your own example, you say "In space, no one can hear you scream", not
"In location space, group-of-people no one can hear you scream". So why is
it a problem that we say "myString, capitalized with myLocale" instead of
"myString, string capitalized with locale myLocale"? These are redundancies
that we would never tolerate in natural language; I don't see why you think
code should be different.

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
*(subject "timeInterval" and object "Date" both still present; but
oddly, preposition "since" got left outside of the parentheses)*

This is actually inaccurate—the parameter to `timeIntervalSince(_:)` is
unlabeled, so it's:

new: myDate.timeIntervalSince(myDate)

(5) Array:

    old: myArray.forEach({ thing in code})

new: myArray.forEach() { thing in //code }

            *(preposition “for” is outside of the parentheses)*

Yes, because the preposition does not apply to the parameter—it applies
to the operation as a whole. I'll have more to say on this in a moment.

The inconsistency between the examples is shown in the bold text of each
example, but lets go over why this matters. It matters because any language
is easier to learn the more consistently it sticks to its own rules.

This is true, but you aren't just proposing sticking more closely to our
existing standards—you're proposing *changing* our standards. And I don't
think the changes you propose are an improvement. In fact, I'd say each of
these examples is worse:

(1) myCKDatabase.fetchRecord(withRecordID:)

"Fetch record with record ID"? I mean, you could at least remove the
`Record` before `ID`. What other ID do you suppose it would be?

I *can* see the case for going back to `fetchRecord` instead of just
`fetch`, though. On the other hand, I do think that, if you know it's a
database, the most obvious thing for `fetch` to be fetching is a record
from that database. It's not a dog—it won't be fetching a stick.

(2) myString.stringCapitalized(withLocale:)

Let's translate this to an actual use site, which is what we care about.

func tableView(_: UITableView, titleForHeaderInSection section: Int) ->
String? {
return sections[section].title.stringCapitalized(withLocale: .current)
}

What is `string` contributing here? We already know it's a "title",
which sounds a lot like a string. If you asked for a "capitalized" string,
what else would you get back if not another string?

The locale parameter is a little more tricky. You're right that `(with:
.current)` is vague, but I can imagine plenty of call sites where `with`
wouldn't be:

title.capitalized(with: german)
title.capitalized(with: docLocale)
title.capitalized(with: otherUser.locale)

Something at the call site needs to imply this is a locale, and there's
nothing in `(with: .current)` that does so. This is arguably just a style
issue, though: even though the language allows you to say `(with:
.current)`, you really ought to say `(with: Locale.current)` to be clearer.
Or perhaps the problem is with the name `current`—it ought to be
`currentLocale` (even though that's redundant when you write it out as
`Locale.currentLocale`), or it should use some location-ish terminology
like `home` or `native`.

(Actually, I think there might be a new guideline there: Variable and
property names should at least hint at the type of the value they contain.
Names like `current` or `shared` or `default` are too vague, and should
usually be paired with a word that implies the type.)

It might also help to change the `with` preposition to `in`, which would
at least imply that the parameter is related to some kind of location.

title.capitalized(in: german)
title.capitalized(in: docLocale)
title.capitalized(in: otherUser.locale)
title.capitalized(in: .current) // Still not great, but better

(3) myDictionary.elementRemoved(atIndex:)

This naming is exactly backwards, and is a perfect example of why we
*don't* want rigid consistency:

1. It emphasizes the element being returned, while making the "Removed"
operation an afterthought, even though the removal is the main thing you
want to happen and the element is returned as an afterthought.

2. It mentions the "Index", even though there is no other plausible
thing that could be labeled "at". (The only other plausible parameters to a
`remove` method are an element, an array of elements, a predicate, or a
range of indices. Of those four, only the range of indices could possibly
make sense with "at", but that ambiguity is a harmless overloading.)

Again, think about a use site:

func tableView(_: UITableView, commit edit: UITableViewCellEditingStyle,
forRowAt indexPath: IndexPath) {
assert(edit == .delete)
sections[indexPath.section].rows.elementRemoved(atIndex: indexPath.row)
// vs.
sections[indexPath.section].rows.remove(at: indexPath.row)
}

In context, `elementRemoved` obscures the purpose of the line, and there
is no ambiguity about what `at` means. The current name is *far* better.

(4) myDate.timeInterval(sinceDate:)

I have a hard time thinking of a call site where `sinceDate` would be an
improvement over `since`; the preposition already *strongly* implies a
temporal aspect to the parameter.

If we remove `Date`, then in isolation I kind of like this change. The
problem arrives when you step out of isolation and think about the other
methods in its family. It would be strange to have
`myDate.timeInterval(since: otherDate)`, but `myDate.timeIntervalSinceNow()
`.

One solution would be to add `now`, `referenceDate`, and `unixEpoch` (or
`utc1970`) as static properties on `Date`. Then you could have just the one
`timeInterval(since:)` method:

myDate.timeInterval(since: otherDate)
myDate.timeInterval(since: .now)
myDate.timeInterval(since: .referenceDate)
myDate.timeInterval(since: .unixEpoch)

Another solution would be to add a `-` operator that takes two `Date`s
and returns a `TimeInterval`, sidestepping the wording issue entirely.

(5) myArray.each(inClosure: )

I don't get this name at all. This operation is completely imperative
and side-effectful, but there's no verb? How is it "in" the closure? Why
"closure" when you can pass a function, and in fact you probably will
*only* see this label if you're passing a function?

I do think there's a problem with `forEach(_:)`—it ought to be
`forEach(do:)`. This is much like `DispatchQueue.async(execute:)` or
the `withoutActuallyEscaping(_:do:)` function in the standard library.
When you pass a function parameter, and the call's primary purpose is to
run that parameter, it's often best to attach the verb to that parameter
instead of putting it in the base name. Of course, the complication there
is that the verb doesn't actually get written if you use trailing closure
syntax, but the curlies sort of fill that role. Kind of.

Although I do understand removing "string" from the latter was to reduce
redundancy in function/method declarations, we only make one declaration,
yet we make many calls. So increasing ambiguity in calls does not seem like
a good trade-off for decreased boilerplate in declarations. More often than
not it's calls that we're reading, not the declarations—unless of course
the call was ambiguous and we had to read the declaration to make sense out
of it. So perhaps we might question if increased ambiguity is an overall
good thing.

I think you misunderstand the current Guidelines' goals. The Guidelines
are not trying to reduce redundancy at declaration sites—they're trying to
reduce redundancy at *call sites*. The idea is that, if the variable names
for the method's target and parameters imply something about the types they
contain, those names along with the prepositions will imply the purpose of
each parameter, and therefore the call. The types are just a more formal
version of that check.

That's why the very first paragraph of the API Design Guidelines <
https://swift.org/documentation/api-design-guidelines/> says:

Clarity at the point of use is your most important goal. Entities such
as methods and properties are declared only once but used repeatedly.
Design APIs to make those uses clear and concise. When evaluating a design,
reading a declaration is seldom sufficient; always examine a use case to
make sure it looks clear in context.

So the tradeoff is not between *declaration redundancy* and call site
clarity—it is between *call site redundancy* and call site ambiguity.
Because their parameters are unlabeled, most languages have severe call
site ambiguity problems. Objective-C has a pretty serious call site
redundancy problem. Swift's design is trying to hit the baby bear "just
right" point.

It is quite possible that, in some areas, we have swung too far back
towards ambiguity. But I don't think `elementRemoved(atIndex:)` is going to
fix that.

However this removal of explicit contextual cues from the method
signature harms readability, since now, the compiler will let people write
code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short
variable names or NO variable names. However, spend much time on GitHub or
in CocoaPods and you will see an increasing number of codebases where
that's exactly what they do, especially in closures.

What I think you're missing with this example—and in fact with all of
your closure-based examples—is that closures don't exist in isolation;
they're in some larger context. (Otherwise, they won't compile.) For
instance, the above closure might be in a line like:

return zip(databases, recordIDs)
.map { return $0.fetch(withRecordID:$1) }

Read in the context of its line, the meanings of $0 and $1 are fairly
clear.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have
no reason to expect the type of "myRecordID" not to be CKRecordID. (In
fact, it's CKRecord.)

Again, in the larger context, this line will end up generating a
`[CKRecord]` array instead of a `[CKRecordID]` array, which is probably
going to cause a type mismatch once you try to actually use the alleged
record IDs. (But as I said earlier, I can see the case for using
`fetchRecord(withID:)` or `fetchRecord(with:)` instead of
`fetch(withRecordID:)`.)

Ambiguous names can hide bugs in their ambiguity, but verbose names can
also hide bugs in the sheer mass of code they generate. The difference is,
developers can manage the ambiguity in their code by naming variables well,
but they can't manage verbosity if verbose names are imposed on them.

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

Again, the context of the closure would tell you, but part of the
problem here is that they held onto an Objective-C preposition which was
poorly chosen. If the line were `$0.draw(in:$1)`, you would know that
`$1` specified an area of the screen and `$0` was something that could be
drawn, which frankly is all you really *need* to know to understand what
this line does.

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value
pair, array will be a dict, and key will be an index type! This may seem
like a ridiculous example, but I have literally seen things like this.

Where have you seen something like this? `array` would have to be passed
`inout` for this to work at all.

Nevertheless, how would more verbose names help with this problem? This
is every bit as incorrect, and the compiler will still accept it:

{array, key in
let number = array.elementRemoved(atIndex:key)
return number }

Are you thinking that they'll notice that `atIndex` is not `atKey`?
There is already a much stronger safeguard against that: `Dictionary.Index`
and `Dictionary.Key` are different, totally incompatible types. Every
mechanism I can think of to get a `Dictionary.Index` has "index" or
"indices" in its name, so you could only make this mistake if you confused
dictionary indices with dictionary keys, in which case `atIndex:` would not
stand out to you either.

Ultimately, unless the compiler actually understands identifiers, it's
just not going to be able to catch mistakes like calling a dictionary an
"array", or many of the other problems you describe here. But the type
system can and often does flag these kinds of problems pretty close to the
source.

Orphaning method signatures by stripping useful return type and argument
type information wouldn't be so bad if variables were all named
descriptively, but that is a strangely optimistic hope for a language
that's as paranoid about safety that it was specifically designed to
prevent many categories of common mistakes.

Personally, I think of Swift's approach to safety as similar to Reagan's
"trust, but verify". Our naming conventions trust the programmer to write
code with clear names; our picky type system verifies that the code is
plausible. We don't force the programmer to explain herself to us until we
notice that something doesn't seem right.

The bottom line is, a language can't force you to write clear and
correct code, but it has many tools it can use to encourage it. Swift
chooses not to use the "heavily redundant names" tool because its cost to
good code is too high. We instead rely more on other tools, like strong
typing, value types, and definite initialization, to encourage high-quality
code at a lower cost.

More verbose rather than heavily redundant names tool, but what have we
lost by being reliant on strongish static typing and value types? Would
Cocoa/ UIKit be so nice to use if rewritten fully in idiomatic non @objc
Swift (compared to working with UI on C++ platforms)? What would it tell us?
Sorry for the rant... do not take static typing laying down j/k ;).
(still quite in “love” with the opt-in security that annotations and clang
warnings bring in Objective-C rather than the opt-out freedom model some
other languages have)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

And likewise! I hope we can continue to work together to make Swift more
dynamic, expressive, and useful in the months and years going forward :).

Best,
Austin

···

On Thu, Aug 3, 2017 at 10:33 AM, Goffredo Marocchi <panajev@gmail.com> wrote:

Thank you for the long reply, I do agree that I could have been more
constructive and what I said could have been more on point with the thread
at hand and better argued. I do like a complete and well written rebuttal,
chance for me to learn from it :).

On Thu, Aug 3, 2017 at 5:14 PM, Austin Zheng <austinzheng@gmail.com> > wrote:

On Thu, Aug 3, 2017 at 8:13 AM, Goffredo Marocchi <panajev@gmail.com> >> wrote:

Because what is the current idiomatic Swift of today may not be the
Swift of tomorrow if people are allowed to keep shaping it as it gets used
in the real world. Ideas can be imported from other languages and today's
ideas and philosophy are not dogmas.

The problem I see with your email is that it does not contribute anything
to the discussion.

- We _know_ Swift has an expressivity problem compared to Objective-C,
many people have made this point loud and clear.
- There are no useful concrete proposals or suggestions of design
directions expressed in this email besides the rehashing of a point that
has been made ad nauseaum on these lists, especially in places more
appropriate for it (e.g. in proposal reviews that involved strengthening
the type system).

Swift does value expressiveness, see the use of argument labels which is
perhaps more pervasive than it was in Objective-C (even more before the
change in argument labels that hit closures and callbacks).

Here is the crux of my disagreement with your email.

Method naming conventions and static typing can be adjusted nearly
independently of one another, and bringing up the latter in a discussion
about the former is _not helpful_.

You could remove Swift's type system tomorrow and still improve how
methods are named. In fact, Swift's current naming conventions are quite
suited for a dynamic language. You could also strengthen Swift's type
system and improve method naming conventions. The two do not march in
lockstep. "Expressiveness" is a huge, vague, and wide-reaching topic.
Claiming that static typing is an expressiveness concern and therefore that
complaining about it in a thread that deals with an almost completely
unrelated feature strikes me as disingenuous. Help me out here.

I do not want to add noise to useful discussions so please do not assume
the worst in my statements, but I also do not think that the idea of "if
you do not like <a>, go back to your <insert expletive> country" has merit
or is helpful to the evolution of the language.

Please.

The issue at hand is not "go back to your country", it is "use the right
tool for the job at hand".

Engineering, like every field of human endeavor, has political elements,
but it is not itself a fundamentally political endeavor. This gross
anthropomorphization is beneath both you and I, and is better avoided for
the happiness of all.

As for contributing to the evolution of the language, your post is not
the first of its quality, specific form, and sentiment that I've seen on
the list, but only the first that I've been moved to respond to. So if I
can steer future discussion in a more productive direction, I will consider
this email chain to be a net positive, even if I have to send out quite a
few more of these emails in reply :).

With the utmost respect,
Austin

On Thu, Aug 3, 2017 at 3:16 PM, Austin Zheng <austinzheng@gmail.com> >>> wrote:

If you want to use Objective-C you're free to use Objective-C, or more
generally any of the wonderful languages that choose a different tradeoff
regarding convenience over expressiveness. Otherwise, I'm not really sure
what productive forward movement bringing up complaints about a fundamental
philosophical underpinning of Swift that's not realistically going to
change is supposed to achieve. As was mentioned in a message earlier this
week, swift-evolution is a list to discuss making changes to the Swift
language, not a list for ranting about things in Swift you don't like but
cannot change.

Regards,
Austin

On Aug 2, 2017, at 11:43 PM, Goffredo Marocchi via swift-evolution < >>>> swift-evolution@swift.org> wrote:

Sent from my iPhone

On 3 Aug 2017, at 04:39, Brent Royal-Gordon via swift-evolution < >>>> swift-evolution@swift.org> wrote:

On Aug 2, 2017, at 10:49 AM, Xiaodi Wu via swift-evolution < >>>> swift-evolution@swift.org> wrote:

Brent had a draft proposal to revise the names of collection methods to
improve the situation here. There is room for improvement.

It didn't change `remove(at:)`, though. (Well, it might have affected
its return value or something, I don't remember.) It mostly addressed the
`dropLast()`, `prefix(_:)` etc. calls.

To respond to the original post:

Some of the APIs you cite are not very well named, but I think your
diagnosis is incorrect and so your prescription isn't going to help.

The reason for this is, whenever a preposition is used in English, it
almost always takes a dyadic form, relating a subject to the preposition's
object. The two most common dyadic formats are:

*<subject> [<preposition> <object of preposition>]*
<The boy> [<with> <the dog>] crossed the street.

*[<preposition> <** object of preposition**>] <subject>*
[<In> <space>], <no one> can hear you scream.
[<On> <the Moon>] are <many craters>.

Now, in Objective C through Swift 1 and 2, prepositions' dyadic nature
were generally respected in method signatures. However, Swift 3's migration
of the preposition inside the parentheses also seems to have been
accompanied by the stripping away of either the subject, the prepositional
object, or both—according to no discernible pattern. For example:

(1) CloudKit:

old: myCKDatabase.fetchRecordWithID(recordID)
new: myCKDatabase.fetch(withRecordID: recordID)
*(subject "Record" got removed)*

(2) String:

old: myString.capitalizedStringWithLocale(_: myLocale)
new: myString.capitalized(with: myLocale)
*(subject "String" and prep. object "Locale" both got removed)*

(3) Dictionary:

old: myDict.removeAtIndex(myIndex)
new: myDict.remove(at: myIndex)
*(subject "element" already missing from both; prep. object "Index" got
removed)*

The thing is, the subjects and prepositional objects *are* present in
all of these—they are the parameters and targets of the calls.

In your own example, you say "In space, no one can hear you scream",
not "In location space, group-of-people no one can hear you scream". So why
is it a problem that we say "myString, capitalized with myLocale" instead
of "myString, string capitalized with locale myLocale"? These are
redundancies that we would never tolerate in natural language; I don't see
why you think code should be different.

(4) Date:

old: myDate.timeIntervalSinceDate(myDate)
new: myDate.timeIntervalSince(date: myDate)
*(subject "timeInterval" and object "Date" both still present; but
oddly, preposition "since" got left outside of the parentheses)*

This is actually inaccurate—the parameter to `timeIntervalSince(_:)` is
unlabeled, so it's:

new: myDate.timeIntervalSince(myDate)

(5) Array:

    old: myArray.forEach({ thing in code})

new: myArray.forEach() { thing in //code }

            *(preposition “for” is outside of the parentheses)*

Yes, because the preposition does not apply to the parameter—it applies
to the operation as a whole. I'll have more to say on this in a moment.

The inconsistency between the examples is shown in the bold text of
each example, but lets go over why this matters. It matters because any
language is easier to learn the more consistently it sticks to its own
rules.

This is true, but you aren't just proposing sticking more closely to
our existing standards—you're proposing *changing* our standards. And I
don't think the changes you propose are an improvement. In fact, I'd say
each of these examples is worse:

(1) myCKDatabase.fetchRecord(withRecordID:)

"Fetch record with record ID"? I mean, you could at least remove the
`Record` before `ID`. What other ID do you suppose it would be?

I *can* see the case for going back to `fetchRecord` instead of just
`fetch`, though. On the other hand, I do think that, if you know it's a
database, the most obvious thing for `fetch` to be fetching is a record
from that database. It's not a dog—it won't be fetching a stick.

(2) myString.stringCapitalized(withLocale:)

Let's translate this to an actual use site, which is what we care about.

func tableView(_: UITableView, titleForHeaderInSection section: Int) ->
String? {
return sections[section].title.stringCapitalized(withLocale: .current)
}

What is `string` contributing here? We already know it's a "title",
which sounds a lot like a string. If you asked for a "capitalized" string,
what else would you get back if not another string?

The locale parameter is a little more tricky. You're right that `(with:
.current)` is vague, but I can imagine plenty of call sites where `with`
wouldn't be:

title.capitalized(with: german)
title.capitalized(with: docLocale)
title.capitalized(with: otherUser.locale)

Something at the call site needs to imply this is a locale, and there's
nothing in `(with: .current)` that does so. This is arguably just a style
issue, though: even though the language allows you to say `(with:
.current)`, you really ought to say `(with: Locale.current)` to be clearer.
Or perhaps the problem is with the name `current`—it ought to be
`currentLocale` (even though that's redundant when you write it out as
`Locale.currentLocale`), or it should use some location-ish terminology
like `home` or `native`.

(Actually, I think there might be a new guideline there: Variable and
property names should at least hint at the type of the value they contain.
Names like `current` or `shared` or `default` are too vague, and should
usually be paired with a word that implies the type.)

It might also help to change the `with` preposition to `in`, which
would at least imply that the parameter is related to some kind of location.

title.capitalized(in: german)
title.capitalized(in: docLocale)
title.capitalized(in: otherUser.locale)
title.capitalized(in: .current) // Still not great, but better

(3) myDictionary.elementRemoved(atIndex:)

This naming is exactly backwards, and is a perfect example of why we
*don't* want rigid consistency:

1. It emphasizes the element being returned, while making the "Removed"
operation an afterthought, even though the removal is the main thing you
want to happen and the element is returned as an afterthought.

2. It mentions the "Index", even though there is no other plausible
thing that could be labeled "at". (The only other plausible parameters to a
`remove` method are an element, an array of elements, a predicate, or a
range of indices. Of those four, only the range of indices could possibly
make sense with "at", but that ambiguity is a harmless overloading.)

Again, think about a use site:

func tableView(_: UITableView, commit edit: UITableViewCellEditingStyle,
forRowAt indexPath: IndexPath) {
assert(edit == .delete)
sections[indexPath.section].rows.elementRemoved(atIndex: indexPath.row)
// vs.
sections[indexPath.section].rows.remove(at: indexPath.row)
}

In context, `elementRemoved` obscures the purpose of the line, and
there is no ambiguity about what `at` means. The current name is *far*
better.

(4) myDate.timeInterval(sinceDate:)

I have a hard time thinking of a call site where `sinceDate` would be
an improvement over `since`; the preposition already *strongly* implies a
temporal aspect to the parameter.

If we remove `Date`, then in isolation I kind of like this change. The
problem arrives when you step out of isolation and think about the other
methods in its family. It would be strange to have
`myDate.timeInterval(since: otherDate)`, but `myDate.timeIntervalSinceNow()
`.

One solution would be to add `now`, `referenceDate`, and `unixEpoch`
(or `utc1970`) as static properties on `Date`. Then you could have just the
one `timeInterval(since:)` method:

myDate.timeInterval(since: otherDate)
myDate.timeInterval(since: .now)
myDate.timeInterval(since: .referenceDate)
myDate.timeInterval(since: .unixEpoch)

Another solution would be to add a `-` operator that takes two `Date`s
and returns a `TimeInterval`, sidestepping the wording issue entirely.

(5) myArray.each(inClosure: )

I don't get this name at all. This operation is completely imperative
and side-effectful, but there's no verb? How is it "in" the closure? Why
"closure" when you can pass a function, and in fact you probably will
*only* see this label if you're passing a function?

I do think there's a problem with `forEach(_:)`—it ought to be
`forEach(do:)`. This is much like `DispatchQueue.async(execute:)` or
the `withoutActuallyEscaping(_:do:)` function in the standard library.
When you pass a function parameter, and the call's primary purpose is to
run that parameter, it's often best to attach the verb to that parameter
instead of putting it in the base name. Of course, the complication there
is that the verb doesn't actually get written if you use trailing closure
syntax, but the curlies sort of fill that role. Kind of.

Although I do understand removing "string" from the latter was to
reduce redundancy in function/method declarations, we only make one
declaration, yet we make many calls. So increasing ambiguity in calls does
not seem like a good trade-off for decreased boilerplate in declarations.
More often than not it's calls that we're reading, not the
declarations—unless of course the call was ambiguous and we had to read the
declaration to make sense out of it. So perhaps we might question if
increased ambiguity is an overall good thing.

I think you misunderstand the current Guidelines' goals. The Guidelines
are not trying to reduce redundancy at declaration sites—they're trying to
reduce redundancy at *call sites*. The idea is that, if the variable names
for the method's target and parameters imply something about the types they
contain, those names along with the prepositions will imply the purpose of
each parameter, and therefore the call. The types are just a more formal
version of that check.

That's why the very first paragraph of the API Design Guidelines <
https://swift.org/documentation/api-design-guidelines/> says:

Clarity at the point of use is your most important goal. Entities such
as methods and properties are declared only once but used repeatedly.
Design APIs to make those uses clear and concise. When evaluating a design,
reading a declaration is seldom sufficient; always examine a use case to
make sure it looks clear in context.

So the tradeoff is not between *declaration redundancy* and call site
clarity—it is between *call site redundancy* and call site ambiguity.
Because their parameters are unlabeled, most languages have severe call
site ambiguity problems. Objective-C has a pretty serious call site
redundancy problem. Swift's design is trying to hit the baby bear "just
right" point.

It is quite possible that, in some areas, we have swung too far back
towards ambiguity. But I don't think `elementRemoved(atIndex:)` is going to
fix that.

However this removal of explicit contextual cues from the method
signature harms readability, since now, the compiler will let people write
code like:

{ return $0.fetch(withRecordID:$1) }

Clearly, the onus is now on the developer not to use cryptic, short
variable names or NO variable names. However, spend much time on GitHub or
in CocoaPods and you will see an increasing number of codebases where
that's exactly what they do, especially in closures.

What I think you're missing with this example—and in fact with all of
your closure-based examples—is that closures don't exist in isolation;
they're in some larger context. (Otherwise, they won't compile.) For
instance, the above closure might be in a line like:

return zip(databases, recordIDs)
.map { return $0.fetch(withRecordID:$1) }

Read in the context of its line, the meanings of $0 and $1 are fairly
clear.

Another problem is that the compiler doesn't care if you write:

{ ambiguousName in
let myRecordID = ambiguousName.fetch(withRecordID:myID)
return myRecordID }

This is highly problematic because someone reading this code will have
no reason to expect the type of "myRecordID" not to be CKRecordID. (In
fact, it's CKRecord.)

Again, in the larger context, this line will end up generating a
`[CKRecord]` array instead of a `[CKRecordID]` array, which is probably
going to cause a type mismatch once you try to actually use the alleged
record IDs. (But as I said earlier, I can see the case for using
`fetchRecord(withID:)` or `fetchRecord(with:)` instead of
`fetch(withRecordID:)`.)

Ambiguous names can hide bugs in their ambiguity, but verbose names can
also hide bugs in the sheer mass of code they generate. The difference is,
developers can manage the ambiguity in their code by naming variables well,
but they can't manage verbosity if verbose names are imposed on them.

We also have examples like:

{ return $0.draw(with:$1) }

What is $0? What is $1? This is a real Apple API, BTW.

Again, the context of the closure would tell you, but part of the
problem here is that they held onto an Objective-C preposition which was
poorly chosen. If the line were `$0.draw(in:$1)`, you would know that
`$1` specified an area of the screen and `$0` was something that could be
drawn, which frankly is all you really *need* to know to understand what
this line does.

{array, key in
let number = array.remove(at:key)
return number }

This will compile and run even though number will be a tuple key-value
pair, array will be a dict, and key will be an index type! This may seem
like a ridiculous example, but I have literally seen things like this.

Where have you seen something like this? `array` would have to be
passed `inout` for this to work at all.

Nevertheless, how would more verbose names help with this problem? This
is every bit as incorrect, and the compiler will still accept it:

{array, key in
let number = array.elementRemoved(atIndex:key)
return number }

Are you thinking that they'll notice that `atIndex` is not `atKey`?
There is already a much stronger safeguard against that: `Dictionary.Index`
and `Dictionary.Key` are different, totally incompatible types. Every
mechanism I can think of to get a `Dictionary.Index` has "index" or
"indices" in its name, so you could only make this mistake if you confused
dictionary indices with dictionary keys, in which case `atIndex:` would not
stand out to you either.

Ultimately, unless the compiler actually understands identifiers, it's
just not going to be able to catch mistakes like calling a dictionary an
"array", or many of the other problems you describe here. But the type
system can and often does flag these kinds of problems pretty close to the
source.

Orphaning method signatures by stripping useful return type and
argument type information wouldn't be so bad if variables were all named
descriptively, but that is a strangely optimistic hope for a language
that's as paranoid about safety that it was specifically designed to
prevent many categories of common mistakes.

Personally, I think of Swift's approach to safety as similar to
Reagan's "trust, but verify". Our naming conventions trust the programmer
to write code with clear names; our picky type system verifies that the
code is plausible. We don't force the programmer to explain herself to us
until we notice that something doesn't seem right.

The bottom line is, a language can't force you to write clear and
correct code, but it has many tools it can use to encourage it. Swift
chooses not to use the "heavily redundant names" tool because its cost to
good code is too high. We instead rely more on other tools, like strong
typing, value types, and definite initialization, to encourage high-quality
code at a lower cost.

More verbose rather than heavily redundant names tool, but what have we
lost by being reliant on strongish static typing and value types? Would
Cocoa/ UIKit be so nice to use if rewritten fully in idiomatic non @objc
Swift (compared to working with UI on C++ platforms)? What would it tell us?
Sorry for the rant... do not take static typing laying down j/k ;).
(still quite in “love” with the opt-in security that annotations and clang
warnings bring in Objective-C rather than the opt-out freedom model some
other languages have)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Terms of Service

Privacy Policy

Cookie Policy