[Proposal] Explicit Non-Default-Implemented Protocol Requirements

Good day, swift community!
This time, I'd like to bring your attention to an everyday problem which, I'm sure, you've all dealt with at some point.

Motivation

Many protocols have a lot of default-implemented requirements so that they can be customized, which would otherwise be impossible to do.
For example, the Comparable protocol, which (considering its inheritance of Equatable) states a requirement of ==, !=, <, <=, >, and >=, only requires implementation of == and <, because all others have default implementations in terms of these two. This forces the user to either memorize the exact set of mandatory requirements or, in case of more complicated protocols like Collection, constantly look up the documentation and hope that it has a section outlining how to conform to it.
Conversely, the author of the protocol needs to manually keep track of the requirements and their default implementations to ensure that the ones that have to be mandatory stay mandatory and the rest of the requirements stay default-implemented, which is also a tedious and error-prone work.

Proposed Solution

I'd like to propose the ability to mark select protocol requirements as default, which would cause the following effects:
The compiler will emit a compile-time error if a default implementation is not available in a non-constrained protocol extension in the same file and provide a fix-it for inserting a template.
If a conforming type does not implement all requirements of the protocol, the compiler will not produce fix-its for inserting a template for any default requirements.

Example

// Equatable.swift

protocol Equatable {

  static func == (_ some: Self, _ other: Self) -> Bool

  default static func != (_ some: Self, _ other: Self) -> Bool
  ^ error: missing implementation of a default requirement `!=`; fix-it: insert a default implementation

}

extension Equatable {

  static func != (_ some: Self, _ other: Self) -> Bool {
    [Your Code Here]
  }

}

// Something.swift

struct Something: Equatable {
^ error: missing implementation of a non-default requirement `==`; fix-it: insert an implementation

}

extension Something {

  static func == (_ some: Something, _ other: Something) -> Bool {
    [Your Code Here]
  }

}

Cheers,
Gor Gyolchanyan.

The problem is real, but afaics, there is some consensus that the best solution is to allow default implementations inside the protocol declaration:
Less typing, no new keywords, easy to understand.

- Tino

That would work as well, but it has the downside of forcing a potentially huge number of methods to be implemented in a single place, reducing the readability as opposed to packing them into semantically related groups in the form of extensions.

Also, please include the original message for reference purposes.

···

On Aug 2, 2017, at 12:18 PM, Tino Heth <2th@gmx.de> wrote:

The problem is real, but afaics, there is some consensus that the best solution is to allow default implementations inside the protocol declaration:
Less typing, no new keywords, easy to understand.

- Tino

Besides the comments below on consensus about how to deal with the
documentation aspect, I should point out that your example is incorrect. !=
is not a protocol requirement of Equatable with a default implementation,
but a protocol extension method that cannot be overridden which is *not* a
protocol requirement. That is by design.

Additionally, it is unclear to me why default implementations should need
be provided in the same file, as your solution requires. There are good
reasons why library A that builds upon library B might supply a default
implementation in an extension for a protocol B.P that refines B.Q. To the
end user, it is just as much of a default that doesn’t need to be
implemented when conforming to P, but your proposed solution does nothing
to address this issue.

···

On Wed, Aug 2, 2017 at 03:54 Gor Gyolchanyan via swift-evolution < swift-evolution@swift.org> wrote:

Good day, swift community!
This time, I'd like to bring your attention to an everyday problem which,
I'm sure, you've all dealt with at some point.

*Motivation*

Many protocols have a lot of default-implemented requirements so that they
can be customized, which would otherwise be impossible to do.
For example, the Comparable protocol, which (considering its inheritance
of Equatable) states a requirement of ==, !=, <, <=, >, and >=, only
requires implementation of == and <, because all others have default
implementations in terms of these two. This forces the user to either
memorize the exact set of mandatory requirements or, in case of more
complicated protocols like Collection, constantly look up the
documentation and hope that it has a section outlining how to conform to it.
Conversely, the author of the protocol needs to manually keep track of the
requirements and their default implementations to ensure that the ones that
have to be mandatory stay mandatory and the rest of the requirements stay
default-implemented, which is also a tedious and error-prone work.

*Proposed Solution*

I'd like to propose the ability to mark select protocol requirements as
default, which would cause the following effects:

   - The compiler will emit a compile-time error if a default
   implementation is not available in a non-constrained protocol extension in
   the same file and provide a fix-it for inserting a template.
   - If a conforming type does not implement all requirements of the
   protocol, the compiler will not produce fix-its for inserting a template
   for any default requirements.

*Example*

// Equatable.swift

protocol Equatable {

static func == (_ some: Self, _ other: Self) -> Bool

default static func != (_ some: Self, _ other: Self) -> Bool
*^ error: missing implementation of a default requirement `!=`; fix-it:
insert a default implementation*

}

extension Equatable {

static func != (_ some: Self, _ other: Self) -> Bool {
*[Your Code Here]*
}

}

// Something.swift

struct Something: Equatable {
*^ error: missing implementation of a non-default requirement
`==`; fix-it: insert an implementation*

}

extension Something {

static func == (_ some: Something, _ other: Something) -> Bool {
*[Your Code Here]*
}

}

Cheers,
Gor Gyolchanyan.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I agree with this, extensions on types defined in the same file are
generally silly, and default implementations belong in the protocol body. I
don’t agree with certain style guides prescription of same-file extensions;
they should only be used to hide protocol conformances that are
“unimportant” to the functionality of the type. In practice this means only
conformances like CustomStringConvertible and CustomDebugStringConvertible
live in extensions.

If you want to group related methods, use linebreaks and whitespace for
that. Don’t split up the type braces since that messes up the whole
one-set-of-braces == one type definition visual rule.

The only time it ever makes sense to extend a non concrete type that you
own is when adding conditional default implementations. Having to extend a
bare protocol is the product of a language limitation.

···

On Wed, Aug 2, 2017 at 6:26 AM, Tino Heth via swift-evolution < swift-evolution@swift.org> wrote:

That would work as well, but it has the downside of forcing a potentially
huge number of methods to be implemented in a single place, reducing the
readability as opposed to packing them into semantically related groups in
the form of extensions.

I really don't get why people are so obsessed with same-file extensions:
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 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.
[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, and if
you have a huge entity, it doesn't get better just because you split it and
hide its complexity.

Also, please include the original message for reference purposes.

[hopes Discourse will happen soon :wink: ]

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

That would work as well, but it has the downside of forcing a potentially huge number of methods to be implemented in a single place, reducing the readability as opposed to packing them into semantically related groups in the form of extensions.

I really don't get why people are so obsessed with same-file extensions:
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 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.
[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, and if you have a huge entity, it doesn't get better just because you split it and hide its complexity.

Also, please include the original message for reference purposes.

[hopes Discourse will happen soon :wink: ]

In my organization we use same file extensions to group methods by
visibility (and also by protocol conformance), so instead of:

public class MyClass {
    public func method1() {}
    public func method2() {}
    public func method3() {}
    public func method4() {}
    public func method5() {}
    private func method6() {}
    private func method7() {}
    private func method8() {}
    private func method9() {}
}

We write:

public class MyClass {
}

public extension MyClass {
    func method1() {}
    func method2() {}
    func method3() {}
    func method4() {}
    func method5() {}
}

private extension MyClass {
    func method6() {}
    func method7() {}
    func method8() {}
    func method9() {}
}

The main reason is not having to repeat the visibility modifier, although
this brings several other advantages and makes our code style more
consistent and easier to follow:

- It forces us to put the private methods at the end of the file, and
that's good because they are implementation details and thus not the first
thing a reader should see. You could do this with comments, but the
syntactic separation seems stronger.
- It makes trivial switching the visibility of the "public methods". For
example we extracted some frameworks from our app and thanks to this
organization it was trivial to change every internal definition to public
ones.
- It also works with other modifiers like @objc.

···

On Wed, Aug 2, 2017 at 12:26 PM, Tino Heth via swift-evolution < swift-evolution@swift.org> wrote:

That would work as well, but it has the downside of forcing a potentially
huge number of methods to be implemented in a single place, reducing the
readability as opposed to packing them into semantically related groups in
the form of extensions.

I really don't get why people are so obsessed with same-file extensions:
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 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.
[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, and if
you have a huge entity, it doesn't get better just because you split it and
hide its complexity.

--
Víctor Pimentel

That would work as well, but it has the downside of forcing a potentially huge number of methods to be implemented in a single place, reducing the readability as opposed to packing them into semantically related groups in the form of extensions.

I really don't get why people are so obsessed with same-file extensions:

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.

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.

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.

[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.

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.

Also, please include the original message for reference purposes.

[hopes Discourse will happen soon :wink: ]

Original Post: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170731/038457.html

···

On Aug 2, 2017, at 1:26 PM, Tino Heth <2th@gmx.de> wrote:

Besides the comments below on consensus about how to deal with the documentation aspect, I should point out that your example is incorrect. != is not a protocol requirement of Equatable with a default implementation, but a protocol extension method that cannot be overridden which is *not* a protocol requirement. That is by design.

Additionally, it is unclear to me why default implementations should need be provided in the same file, as your solution requires. There are good reasons why library A that builds upon library B might supply a default implementation in an extension for a protocol B.P that refines B.Q. To the end user, it is just as much of a default that doesn’t need to be implemented when conforming to P, but your proposed solution does nothing to address this issue.

Yes, it is restrictive. My approach to open-ended proposals like this (ones that might have unforeseen consequences) is to start with the bare minimum and work outwards from there. If the default implementation is in the same file, then the meta-information about the protocol requirement can be safely cached after compiling an individual file and it would remain valid when linking against the rest of object files in the module. I may, however, be completely wrong in how this works, so that's why I encourage people to put the proposed limitations to question. By the way, thank you for doing that :slightly_smiling_face:

···

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

On Wed, Aug 2, 2017 at 03:54 Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Good day, swift community!
This time, I'd like to bring your attention to an everyday problem which, I'm sure, you've all dealt with at some point.

Motivation

Many protocols have a lot of default-implemented requirements so that they can be customized, which would otherwise be impossible to do.
For example, the Comparable protocol, which (considering its inheritance of Equatable) states a requirement of ==, !=, <, <=, >, and >=, only requires implementation of == and <, because all others have default implementations in terms of these two. This forces the user to either memorize the exact set of mandatory requirements or, in case of more complicated protocols like Collection, constantly look up the documentation and hope that it has a section outlining how to conform to it.
Conversely, the author of the protocol needs to manually keep track of the requirements and their default implementations to ensure that the ones that have to be mandatory stay mandatory and the rest of the requirements stay default-implemented, which is also a tedious and error-prone work.

Proposed Solution

I'd like to propose the ability to mark select protocol requirements as default, which would cause the following effects:
The compiler will emit a compile-time error if a default implementation is not available in a non-constrained protocol extension in the same file and provide a fix-it for inserting a template.
If a conforming type does not implement all requirements of the protocol, the compiler will not produce fix-its for inserting a template for any default requirements.

Example

// Equatable.swift

protocol Equatable {

  static func == (_ some: Self, _ other: Self) -> Bool

  default static func != (_ some: Self, _ other: Self) -> Bool
  ^ error: missing implementation of a default requirement `!=`; fix-it: insert a default implementation

}

extension Equatable {

  static func != (_ some: Self, _ other: Self) -> Bool {
    [Your Code Here]
  }

}

// Something.swift

struct Something: Equatable {
^ error: missing implementation of a non-default requirement `==`; fix-it: insert an implementation

}

extension Something {

  static func == (_ some: Something, _ other: Something) -> Bool {
    [Your Code Here]
  }

}

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

I agree with this, extensions on types defined in the same file are generally silly, and default implementations belong in the protocol body. I don’t agree with certain style guides prescription of same-file extensions; they should only be used to hide protocol conformances that are “unimportant” to the functionality of the type. In practice this means only conformances like CustomStringConvertible and CustomDebugStringConvertible live in extensions.

If you want to group related methods, use linebreaks and whitespace for that. Don’t split up the type braces since that messes up the whole one-set-of-braces == one type definition visual rule.

The only time it ever makes sense to extend a non concrete type that you own is when adding conditional default implementations. Having to extend a bare protocol is the product of a language limitation.

Take a look at my replies to Tino Heth about code locality and the rest...

···

On Aug 2, 2017, at 6:15 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Wed, Aug 2, 2017 at 6:26 AM, Tino Heth via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

That would work as well, but it has the downside of forcing a potentially huge number of methods to be implemented in a single place, reducing the readability as opposed to packing them into semantically related groups in the form of extensions.

I really don't get why people are so obsessed with same-file extensions:
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 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.
[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, and if you have a huge entity, it doesn't get better just because you split it and hide its complexity.

Also, please include the original message for reference purposes.

[hopes Discourse will happen soon :wink: ]

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

I don’t understand how extensions help with code locality. It’s literally a
matter of *curly braces*. I already glue the extension block to the bottom }
of the protocol block anyway by omitting the empty linebreak.

protocol Protocol
{
    func default_function()
}
extension Protocol
{
    func default_function()
    {
    }
}

as opposed to

protocol Protocol
{
    func default_function()
    {
    }
}

I would very much appreciate not having to retype Protocol and
default_function() over and over. The whole cult-of-same-file-extensions
seems to be people refusing to let go of old C++ habits. Have you noticed
that nowhere else in Swift is there a concept of separating type
declarations from type definitions? That’s because people managed to figure
out that typing the same words over and over again doesn’t actually keep
code neater at all.

···

On Wed, Aug 2, 2017 at 11:27 AM, Gor Gyolchanyan < gor.f.gyolchanyan@icloud.com> wrote:

On Aug 2, 2017, at 6:15 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

I agree with this, extensions on types defined in the same file are
generally silly, and default implementations belong in the protocol body. I
don’t agree with certain style guides prescription of same-file extensions;
they should only be used to hide protocol conformances that are
“unimportant” to the functionality of the type. In practice this means only
conformances like CustomStringConvertible and CustomDebugStringConvertible
live in extensions.

If you want to group related methods, use linebreaks and whitespace for
that. Don’t split up the type braces since that messes up the whole
one-set-of-braces == one type definition visual rule.

The only time it ever makes sense to extend a non concrete type that you
own is when adding conditional default implementations. Having to extend a
bare protocol is the product of a language limitation.

Take a look at my replies to Tino Heth about code locality and the rest...

On Wed, Aug 2, 2017 at 6:26 AM, Tino Heth via swift-evolution < > swift-evolution@swift.org> wrote:

That would work as well, but it has the downside of forcing a potentially
huge number of methods to be implemented in a single place, reducing the
readability as opposed to packing them into semantically related groups in
the form of extensions.

I really don't get why people are so obsessed with same-file extensions:
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 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.
[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, and if
you have a huge entity, it doesn't get better just because you split it and
hide its complexity.

Also, please include the original message for reference purposes.

[hopes Discourse will happen soon :wink: ]

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

I have always found it curious that extensions cannot be named. Since same-file extension are considered to be superior to comments as a way of separating concerns, wouldn’t it make sense to allow naming of extensions? Extension names could possibly be included in symbols for entities in the extension, giving a clue as to the reason for the extension, perhaps. Tools with access to extension names could automatically provide a richer programming environment, without relying upon comments.

Unique names for extensions to protocols and classes would open the door to the ability to selectively suppress some extensions when importing a module, which might be very valuable in complex code bases with name collisions, for example. This can be a real problem for extensions to built-in and foundation types.

Objective C requires inclusion of category headers, thus allowing code to manage “extension” (category) namespaces implicitly.

This seem to be an intensional omission of the part of the core team, but I have never heard the rationale for it. In the case of protocol conformance extensions, a name would probably be redundant, so it would sensible to default the name to the protocol(s) being implemented. Names would have to be optional for backward compatibility.

- Chris

···

On Aug 2, 2017, at 9:57 AM, Taylor Swift via swift-evolution <swift-evolution@swift.org> wrote:

I don’t understand how extensions help with code locality. It’s literally a matter of curly braces. I already glue the extension block to the bottom } of the protocol block anyway by omitting the empty linebreak.

protocol Protocol
{
    func default_function()
}
extension Protocol
{
    func default_function()
    {
    }
}

as opposed to

protocol Protocol
{
    func default_function()
    {
    }
}

I would very much appreciate not having to retype Protocol and default_function() over and over. The whole cult-of-same-file-extensions seems to be people refusing to let go of old C++ habits. Have you noticed that nowhere else in Swift is there a concept of separating type declarations from type definitions? That’s because people managed to figure out that typing the same words over and over again doesn’t actually keep code neater at all.

On Wed, Aug 2, 2017 at 11:27 AM, Gor Gyolchanyan <gor.f.gyolchanyan@icloud.com <mailto:gor.f.gyolchanyan@icloud.com>> wrote:

On Aug 2, 2017, at 6:15 PM, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

I agree with this, extensions on types defined in the same file are generally silly, and default implementations belong in the protocol body. I don’t agree with certain style guides prescription of same-file extensions; they should only be used to hide protocol conformances that are “unimportant” to the functionality of the type. In practice this means only conformances like CustomStringConvertible and CustomDebugStringConvertible live in extensions.

If you want to group related methods, use linebreaks and whitespace for that. Don’t split up the type braces since that messes up the whole one-set-of-braces == one type definition visual rule.

The only time it ever makes sense to extend a non concrete type that you own is when adding conditional default implementations. Having to extend a bare protocol is the product of a language limitation.

Take a look at my replies to Tino Heth about code locality and the rest...

On Wed, Aug 2, 2017 at 6:26 AM, Tino Heth via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

That would work as well, but it has the downside of forcing a potentially huge number of methods to be implemented in a single place, reducing the readability as opposed to packing them into semantically related groups in the form of extensions.

I really don't get why people are so obsessed with same-file extensions:
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 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.
[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, and if you have a huge entity, it doesn't get better just because you split it and hide its complexity.

Also, please include the original message for reference purposes.

[hopes Discourse will happen soon :wink: ]

_______________________________________________
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
https://lists.swift.org/mailman/listinfo/swift-evolution

+1, although most times I wanted this because of the impossibility of
defining stored attributes in protocol extensions, let alone private stored
attributes.

···

On Wed, Aug 2, 2017 at 1:09 PM, Gor Gyolchanyan via swift-evolution < swift-evolution@swift.org> wrote:

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.

--
Víctor Pimentel

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.

+1, although most times I wanted this because of the impossibility of defining stored attributes in protocol extensions, let alone private stored attributes.

Considering how private access level works within file scope because of code visibility guarantees (the compiler is guaranteed to be able to see both the type and the extension at the same time), the same could be said about stored properties in extension within file scope. Since bot the type and the extension are defined in the same file, there's no way for the type to get improperly laid out, since all stored properties are still visible at the same time. I wonder what it would take to implement this...

···

On Aug 2, 2017, at 2:20 PM, Víctor Pimentel Rodríguez <vpimentel@tuenti.com> wrote:
On Wed, Aug 2, 2017 at 1:09 PM, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

--
Víctor Pimentel

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" :wink: has been so powerful, we finally ended up with what we have now.

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.

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.

[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?

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 :wink: — but that door is imho closed now.

- Tino

Besides the comments below on consensus about how to deal with the documentation aspect, I should point out that your example is incorrect. != is not a protocol requirement of Equatable with a default implementation, but a protocol extension method that cannot be overridden which is *not* a protocol requirement. That is by design.

Forgot to address this part :slightly_smiling_face:
I didn't know Equatable behaved like that. I know that at least some time ago, Comparable behaved similarly (I'm not sure any more). But the primary use case is Collection.

···

On Aug 2, 2017, at 5:35 PM, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 2, 2017, at 5:30 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Additionally, it is unclear to me why default implementations should need be provided in the same file, as your solution requires. There are good reasons why library A that builds upon library B might supply a default implementation in an extension for a protocol B.P that refines B.Q. To the end user, it is just as much of a default that doesn’t need to be implemented when conforming to P, but your proposed solution does nothing to address this issue.

Yes, it is restrictive. My approach to open-ended proposals like this (ones that might have unforeseen consequences) is to start with the bare minimum and work outwards from there. If the default implementation is in the same file, then the meta-information about the protocol requirement can be safely cached after compiling an individual file and it would remain valid when linking against the rest of object files in the module. I may, however, be completely wrong in how this works, so that's why I encourage people to put the proposed limitations to question. By the way, thank you for doing that :slightly_smiling_face:

On Wed, Aug 2, 2017 at 03:54 Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Good day, swift community!
This time, I'd like to bring your attention to an everyday problem which, I'm sure, you've all dealt with at some point.

Motivation

Many protocols have a lot of default-implemented requirements so that they can be customized, which would otherwise be impossible to do.
For example, the Comparable protocol, which (considering its inheritance of Equatable) states a requirement of ==, !=, <, <=, >, and >=, only requires implementation of == and <, because all others have default implementations in terms of these two. This forces the user to either memorize the exact set of mandatory requirements or, in case of more complicated protocols like Collection, constantly look up the documentation and hope that it has a section outlining how to conform to it.
Conversely, the author of the protocol needs to manually keep track of the requirements and their default implementations to ensure that the ones that have to be mandatory stay mandatory and the rest of the requirements stay default-implemented, which is also a tedious and error-prone work.

Proposed Solution

I'd like to propose the ability to mark select protocol requirements as default, which would cause the following effects:
The compiler will emit a compile-time error if a default implementation is not available in a non-constrained protocol extension in the same file and provide a fix-it for inserting a template.
If a conforming type does not implement all requirements of the protocol, the compiler will not produce fix-its for inserting a template for any default requirements.

Example

// Equatable.swift

protocol Equatable {

  static func == (_ some: Self, _ other: Self) -> Bool

  default static func != (_ some: Self, _ other: Self) -> Bool
  ^ error: missing implementation of a default requirement `!=`; fix-it: insert a default implementation

}

extension Equatable {

  static func != (_ some: Self, _ other: Self) -> Bool {
    [Your Code Here]
  }

}

// Something.swift

struct Something: Equatable {
^ error: missing implementation of a non-default requirement `==`; fix-it: insert an implementation

}

extension Something {

  static func == (_ some: Something, _ other: Something) -> Bool {
    [Your Code Here]
  }

}

Cheers,
Gor Gyolchanyan.
_______________________________________________
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

Consider these two alternative ways of writing the same code:
https://gist.github.com/technogen-gg/17b6d5e13c13080850dceda28cfe1caa

···

On Aug 2, 2017, at 6:57 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

I don’t understand how extensions help with code locality. It’s literally a matter of curly braces. I already glue the extension block to the bottom } of the protocol block anyway by omitting the empty linebreak.

protocol Protocol
{
    func default_function()
}
extension Protocol
{
    func default_function()
    {
    }
}

as opposed to

protocol Protocol
{
    func default_function()
    {
    }
}

I would very much appreciate not having to retype Protocol and default_function() over and over. The whole cult-of-same-file-extensions seems to be people refusing to let go of old C++ habits. Have you noticed that nowhere else in Swift is there a concept of separating type declarations from type definitions? That’s because people managed to figure out that typing the same words over and over again doesn’t actually keep code neater at all.

On Wed, Aug 2, 2017 at 11:27 AM, Gor Gyolchanyan <gor.f.gyolchanyan@icloud.com <mailto:gor.f.gyolchanyan@icloud.com>> wrote:

On Aug 2, 2017, at 6:15 PM, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

I agree with this, extensions on types defined in the same file are generally silly, and default implementations belong in the protocol body. I don’t agree with certain style guides prescription of same-file extensions; they should only be used to hide protocol conformances that are “unimportant” to the functionality of the type. In practice this means only conformances like CustomStringConvertible and CustomDebugStringConvertible live in extensions.

If you want to group related methods, use linebreaks and whitespace for that. Don’t split up the type braces since that messes up the whole one-set-of-braces == one type definition visual rule.

The only time it ever makes sense to extend a non concrete type that you own is when adding conditional default implementations. Having to extend a bare protocol is the product of a language limitation.

Take a look at my replies to Tino Heth about code locality and the rest...

On Wed, Aug 2, 2017 at 6:26 AM, Tino Heth via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

That would work as well, but it has the downside of forcing a potentially huge number of methods to be implemented in a single place, reducing the readability as opposed to packing them into semantically related groups in the form of extensions.

I really don't get why people are so obsessed with same-file extensions:
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 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.
[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, and if you have a huge entity, it doesn't get better just because you split it and hide its complexity.

Also, please include the original message for reference purposes.

[hopes Discourse will happen soon :wink: ]

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

    That would work as well, but it has the downside of forcing a potentially huge
    number of methods to be implemented in a single place, reducing the readability
    as opposed to packing them into semantically related groups in the form of
    extensions.

    I really don't get why people are so obsessed with same-file extensions:
    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 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.
    [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, and if you have
    a huge entity, it doesn't get better just because you split it and hide its
    complexity.

In my organization we use same file extensions to group methods by visibility (and also by protocol conformance), so instead of:

Small offtopic.
Just wanted to note, that access level of 'private' method inside type declaration and method in 'private' extension is not the same.

In second case you have actually *fileprivate* access level for method6..method9.
So, strictly speaking, you can't move/group 'private' methods from type declaration into 'private extension' without explicitly marking each method in extension as 'private'.

I do believe 'private extension' should mean 'real' private access, given other extensions of the type(and the type declaration itself) in same file now see private declarations. But as I understand, this sheep has sailed.

Vladimir.

···

On 02.08.2017 14:11, Víctor Pimentel Rodríguez via swift-evolution wrote:

On Wed, Aug 2, 2017 at 12:26 PM, Tino Heth via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

public class MyClass {
     public func method1() {}
     public func method2() {}
     public func method3() {}
     public func method4() {}
     public func method5() {}
     private func method6() {}
private func method7() {}
private func method8() {}
private func method9() {}
}

We write:

public class MyClass {
}

public extension MyClass {
func method1() {}
     func method2() {}
     func method3() {}
     func method4() {}
     func method5() {}
}

private extension MyClass {
     func method6() {}
     func method7() {}
func method8() {}
     func method9() {}
}

The main reason is not having to repeat the visibility modifier, although this brings several other advantages and makes our code style more consistent and easier to follow:

- It forces us to put the private methods at the end of the file, and that's good because they are implementation details and thus not the first thing a reader should see. You could do this with comments, but the syntactic separation seems stronger.
- It makes trivial switching the visibility of the "public methods". For example we extracted some frameworks from our app and thanks to this organization it was trivial to change every internal definition to public ones.
- It also works with other modifiers like @objc.

--
Víctor Pimentel

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

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" :wink: 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

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
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.

···

On Aug 2, 2017, at 3:33 PM, Tino Heth <2th@gmx.de> wrote:

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 :wink: — but that door is imho closed now.

- Tino

Consider these two alternative ways of writing the same code:
https://gist.github.com/technogen-gg/17b6d5e13c13080850dceda28cfe1caa

Great example — the first is better.
But not because of extensions:
// MARK: - String Compatibility

Terms of Service

Privacy Policy

Cookie Policy