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 <pana...@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 <austinzh...@gmail.com> > wrote: > >> >> >> On Thu, Aug 3, 2017 at 8:13 AM, Goffredo Marocchi <pana...@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 <austinzh...@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 >>>> >>>> >>>> >>> >> >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution