Hi all,
The review of SE-0409: Access-level modifiers on import declarations concluded on September 26th.
The Language Steering group has decided to accept the proposal with a single modification. Namely, in addition to access level modifiers (internal
, public
, etc.), import
declarations will be able to be marked with @usableFromInline
to mark an imported module whose declarations should be available for use in the body of @inlinable
functions, but still restricted from appearing in the public interface of the importing module. The Language Steering group agreed that it did not make sense to force users to abandon the interface-level checking provided by this proposal (by making the import
a public import
) just to use a declaration from an inlinable function body.
Overall reception of the feature was generally positive, at least as to the problem raised being worth solving. There were some questions raised as to whether the proposed solution was on the right track.
Some reviewers wondered whether it was appropriate for global property such as "is this module a public dependency or an implementation detail" to be inferred based on the upper bound of it's imported visibility across all source-file-local import
declarations, and suggested that marking dependencies with this information in, say, the package manifest might be a better approach. While the Language Steering Group does not rule out the possibility of some sort of marking in the build system which could enforce "this module cannot be imported at higher than internal
visibility," the Group felt that:
- The ultimate effect of different
import
access levels was significant enough to warrant some sort of marking in the source file, so that users reading code can easily see which dependencies in the file are permitted to be used in the interface with the corresponding access level(s). - The problem being solved is sufficiently a language-level concern that it should not be only the responsibility of the build system to support/enforce dependency visibility—it should be a first-class language feature that Swift itself allows programmers to express regardless of the build system they are using.
Accepting that some source-level marker (attached to import
declarations) was appropriate, the Language Steering Group considered the concerns about the potential unclarity of the public import Foo
spelling. Some reviewers felt that this spelling incorrectly implied behavior more similar to @_exported
than what was actually proposed. That is, reviewers expected that public import Foo
would 're-export' the contents of the Foo module for use from the importing module's namespace. One reviewer noted that perhaps a more accurate, if more awkward, spelling would be @usableFromPublic
, @usableFromInline
, etc.
The Language Steering Group acknowledges that the public
in public import Foo
has a different meaning than the public
in, say, public struct Bar
. However, the Group elected to stick with the proposed spelling for the following reasons:
- The use of
public import
in library code is likely to be relatively pervasive, and a more heavyweight, explicit spelling like@usableFromPublic
or@usableFrom(public)
or similar would quickly become too noisy. - The possible misinterpretation is relatively benign: users who expect
public import Foo
to re-export Foo's contents from their own module will quickly discover that this is not, in fact, its function, and then be able to correct their usage in whatever way they choose (e.g., by applying@_exported
). - Even if we were to formalize
@_exported
as an official language feature, we would not want to use thepublic import Foo
spelling for such a specialized operation with such wide-reaching implications, and so using thepublic import
spelling for the behavior proposed here does not 'close off' its use for different functionality in a way that is concerning.
There were some further questions and concerns raised about details of the proposal:
- Some felt that the inclusion of the
private
access level for import statements was unnecessarily fine-grained. The Language Steering group felt that the use case of a wrapper file around a library that an author does not want to accidentally 'leak' to the rest of the module was sufficiently justification forprivate import
, and that the alternative of factoring that file out into a separate module was too burdensome to be a suitable solution. - Others noted that
private
andfileprivate
were redundant in this proposal becauseimport
declarations can only appear at file scope, and suggested that the language should just pick one to be used withimport
declarations and ban the other. The Language Steering group does not feel that an arbitrary restriction here is warranted, and thatimport
declarations should be able to be marked as eitherprivate
orfileprivate
just like any other top-level declaration.
Thank you for participation in the evolution process!
Frederick Kellison-Linn
Review Manager