> On Aug 2, 2017, at 3:33 PM, Tino Heth <[email protected]> wrote:
> 
> 
>> The same-file mentality comes from Swift 3, which introduced fileprivate 
>> (which, since Swift 4, got merged into private within a single file scope). 
>> With that feature, implementors don't have to choose between access 
>> protection and code locality and can get both.
> You weren't here when this was discussed, were you?
> It was nearly exactly the other way round:
> Fileprivate has been there for years, it just was called private — and 
> because the "church of extensions" ;-) has been so powerful, we finally ended 
> up with what we have now.

Yes, I was here. The fact that we started off with private behaving the way it 
does now is history now. The point stands: extensions in the same file as the 
type definition have an extra guarantee that enable the compiler to provide 
extra features based on that: the guarantee that the extension and the type 
definition will always be visible simultaneously, thus, implicitly merging the 
extension into the type definition is possible. It's not possible to do in any 
other way, because a file is the only unit of compilation that's more-or-less 
guaranteed to be atomically parsed. A module is separated into files, which can 
be compiled into objects separately, making it impossible for the compiler to 
know if any extension in any of the other files will change the layout of the 
type.

>>> They are recommended in style guides, influencers blog about them, and they 
>>> motivated a ridiculous complex change in the access rights system. Yet I 
>>> haven't seen any evidence that they offer real benefit.
>> 
>> Extensions are a tool for decentralizing code. There are some critical 
>> limits on extensions that make main type definition subject to unavoidable 
>> bloating (required and designated initializers, stored properties, the 
>> deinitializer, and open methods), but everything else is a prime candidate 
>> for decentralizing. Putting as little code as possible into the type 
>> definition and semantically grouping the implementation into extensions 
>> improves readability and maintainability dramatically.
> The thing is: This is just a claim that gets repeated over and over. There is 
> no proof, and I even don't know a single study on that topic.

Take a look at this article: 
https://en.wikipedia.org/wiki/Separation_of_concerns 
<https://en.wikipedia.org/wiki/Separation_of_concerns>

>>> Extensions are great for adding useful helpers to existing types, and still 
>>> allow you to selectively expose details of your own classes — but most 
>>> people seem to ignore those options and focus on something can be done 
>>> better with plain old comments.
>> 
>> Relying on comments for invariants and preconditions is a poor design 
>> decision, because there's no way of enforcing them and the whole integrity 
>> of the code is thrown at the mercy of a human's carefulness (which is a 
>> horrible fate to befall upon any code). By writing the code in such a way 
>> that makes it impossible to be misused (by way of compiler enforcement), the 
>> code becomes resilient and no amount of clumsy usage can lead to incredibly 
>> obscure bugs that would take a week of debugging to catch.
> But extensions are no tool to do so: They have no features that offer any 
> protection, exactly like comments — they are just more typing and don't show 
> up properly in Xcode.
> 
> import UIKit
> 
> class MyViewController: UIViewController {
> }
> 
> extension MyViewController: UITableViewDataSource {
> 
>     func tableView(_ tableView: UITableView, numberOfRowslnSection: Int) -> 
> Int {
>         return 1
>     }
> 
>     func tableView(_ tableView: UITableView, didSelectRowAt: IndexPath) {
>         print("Hu, isn't this a delegate method?")
>     }
> }
> 
> extension MyViewController: UITableViewDelegate {
>     func tableView(_ tableView: UITableView, numberOfRowsInSection: Int) -> 
> Int {
>         return 99
>     }
> 
>     func tableView(_ tableView: UITableView, cellForRowAt indexPath: 
> IndexPath) -> UITableViewCell {
>         let cell = UITableViewCell(style: .default, reuseIdentifier: "")
>         cell.textLabel?.text = "I should get my data from a datasource"
>         return cell
>     }
> }
> 
> This is perfectly valid Swift, and it is build on extensions — but does it 
> increase the quality of the code?
> Same file extensions are nothing but a different pair of parenthesis to 
> surround your code, and since Swift 4, you can shuffle around those 
> delimiters however you like, and it has still the same meaning for the 
> compiler.
> They enforce nothing, and so far, I haven't seen any ideas to increase their 
> power.

The compiler also doesn't stop you from making a wide variety of design 
choices, a comprehensive list of which can be found here: 
https://en.wikipedia.org/wiki/Anti-pattern#Programming 
<https://en.wikipedia.org/wiki/Anti-pattern#Programming>
For instance, have you tried reading the implementation of sin function in 
glibc? It's a very thick spaghetti code, sprinkled with a bucketful of magic 
numbers. It's absolutely impossible to comprehend.

Any language limitation should only be implemented if it passes the threshold 
where the gain of safety and convenience outweighs the loss of flexibility and 
the cost of implementing the limitation.
In your example, the gain of convenience is much lower than the cost of 
implementing it and loss of flexibility.

>>> [sorry for the rant — but I think a critical look at extensions is long 
>>> overdue: I rarely see someone questioning their role, so basically, we are 
>>> making important decisions based on pure superstition]
>>> 
>>> A protocol itself is already a vehicle to group related methods,
>> 
>> A protocol is a vehicle for generic programming and separation of 
>> abstractions.
> True — but does that stop a protocol from being a way to group related 
> methods?

Protocols are not free. They come at a cost of wrapping the object in an 
existential container and make indirect calls via the witness table. Extensions 
on value types are free.

>>>  and if you have a huge entity, it doesn't get better just because you 
>>> split it and hide its complexity.
>> 
>> Splitting and hiding complexity is by far the only reasonable way of dealing 
>> with huge entities. If the entity gains too much responsibility, it's 
>> probably a good idea to split it into several smaller entities. If the 
>> entity contains a large amount of accidental complexity that solely serves 
>> the purpose of enabling a select set of intended features, then it's 
>> probably a good idea to hide the accidental complexity away from users of 
>> the entity.
>> 
>> In fact, that's exactly why I always wished that protocols could get private 
>> requirements that are there solely for use in protocol extensions and are 
>> otherwise hidden from existence. I haven't talked about this in detail 
>> because I don't see a reasonable way of implementing it yet.
> 
> Like protected (you can override/implement, but never call)?
> Guess this will haunt us forever ;-) — but that door is imho closed now.
> 
> 
> - Tino
> 

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to