[swift-evolution] [Proposal][Discussion] Modular Swift
Xiaodi Wu
xiaodi.wu at gmail.com
Tue Feb 21 21:35:50 CST 2017
On Tue, Feb 21, 2017 at 9:28 PM, Robert Widmann <devteam.codafi at gmail.com>
wrote:
>
> On Feb 21, 2017, at 10:03 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> On Tue, Feb 21, 2017 at 8:41 PM, Robert Widmann <devteam.codafi at gmail.com>
> wrote:
>
>>
>> On Feb 21, 2017, at 9:37 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>
>> On Tue, Feb 21, 2017 at 8:22 PM, Robert Widmann <devteam.codafi at gmail.com
>> >wrote:
>>
>>>
>>> On Feb 21, 2017, at 9:13 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>
>>> On Tue, Feb 21, 2017 at 7:59 PM, Robert Widmann via swift-evolution <
>>> swift-evolution at swift.org> wrote:
>>>
>>>>
>>>> On Feb 21, 2017, at 7:36 PM, Nevin Brackett-Rozinsky via
>>>> swift-evolution <swift-evolution at swift.org> wrote:
>>>>
>>>> To my mind, any submodule system for Swift should be designed to
>>>> relieve the pressure for long files, and make it easy to group tightly
>>>> related files into a single unit with shared visibility. That way
>>>> developers can easily organize their code into smaller files while
>>>> utilizing Swift’s pattern of providing protocol conformances in extensions
>>>> and keeping implementation details hidden from the rest of the module at
>>>> large.
>>>>
>>>>
>>>> Wonderful, because that’s absolutely supported by this proposal. To
>>>> group tightly related files into a single unit, simply declare a submodule
>>>> for them and extend it in each of your related files.
>>>>
>>>
>>> It's supported, but it isn't first-class. By this I mean: there are two
>>> distinguishable uses supported by your proposal, lumped together by the
>>> fact that they are both about grouping units of code together. Put crudely,
>>> one use case is grouping lines of code, while the other is about grouping
>>> files of code. The merits of supporting both have already been debated in
>>> this discussion. The issue I'll touch on is supporting both with the same
>>> syntax. The chief drawbacks here are:
>>>
>>>
>>> What exactly would be required to *make* it first class? Referencing
>>> file names in the module declaration?
>>>
>>
>> See below.
>>
>>> - It makes sense to use braces to group lines of code, but it makes no
>>> sense to use braces to group files of code; this just causes entire files
>>> to be indented.
>>>
>>>
>>> If braces aren’t used to demarcate scopes, nesting modules becomes
>>> ambiguous.
>>>
>>
>> Again, let's observe the distinction about grouping files vs. grouping
>> lines.
>>
>> Grouping files does not require braces: if the intended use of your
>> feature were to label files X, Y, and Z as belonging to one submodule and
>> A, B, and C to another, it would not matter if X, Y, and Z belonged to
>> Foo.Bar and A, B, and C to Foo.Bar.Baz: your syntax would not require
>> braces.
>>
>>
>>> It’s important to note that indentation is one particular style. LLVM
>>> code style, in particular, chooses not to indent after namespace
>>> declarations. This issue also crops up when dealing with nested type
>>> declarations, and I distinctly remember it not being a big enough deal to
>>> "fix this" at the time when a proposal to “flatten” these declaration was
>>> brought up.
>>>
>>
>> Mine is not a critique of the syntax itself; I don't particularly care
>> about indents, nor do I mind not indenting namespaces.
>>
>> What I'm saying is, you would not have chosen to require braces if your
>> proposed feature were aimed at making the grouping of files into submodules
>> as simple as possible. You chose to accommodate grouping lines using the
>> same syntax as grouping files over the simplest design for grouping files.
>> Make no mistake, this promotes one use over another.
>>
>>
>> Ah, I see. Yes, one of the stated goals is to become
>> filesystem-independent. We certainly cannot do that by encouraging the
>> alternative.
>>
>
> Swift's current design is deliberately not file system-independent. A
> submodule design built on top of Swift could preserve that. Your draft
> proposal makes two changes: it introduces a design for submodules; and, it
> eliminates files as a unit of code by default (not least by declaring
> `fileprivate` redundant). To my mind, you have presented no justification
> for the second change other than to say that it is a stated goal--but why?
>
>
>
>
>
> - fileprivate access can be recreated by creating a private "utility
> submodule" containing declarations of at most internal access.
>
>
>
>
>> - Because some lines of code necessarily precede some other lines of
>>> code, it makes sense to declare the first group using `module` and to
>>> extend that with the second group using `extension`. However, because a
>>> file of code does not necessarily precede another file of code, it is
>>> arbitrary which file is surrounded with a `module` declaration and which
>>> one is surrounded with an `extension` declaration.
>>>
>>>
>>> Absolutely. But it is similarly arbitrary which public APIs are exposed
>>> in a type declaration and which are exposed in an extension declaration.
>>>
>>
>> Not entirely, no. Stored properties must be in the type declaration. Enum
>> cases must be in the type declaration. Perhaps you regard these as
>> temporary inconveniences of the current grammar; I see them as quite
>> reasonable ways to give some consistency as to what's written where in a
>> language where types can be retroactively extended. In a very real sense,
>> you must read the type declaration before you read the extensions in order
>> to understand the latter. By comparison, there is nothing that must be in
>> your proposed module declaration.
>>
>>
>>> My hope is that the module declaration itself will become the
>>> one-stop-shop for re-exports and general public bookkeeping just as
>>> aggregate declarations are today. Module extensions exist to accommodate
>>> users that wish to break related functionality across files or into
>>> separate independent regions within the same file for the same reasons type
>>> extensions exist.
>>>
>>
>> Indeed, that you phrase it this way supports Nevin's argument. _Module
>> extensions_ exist to accommodate his use case; however, his use case
>> (which, mind you, is what I think most people are thinking of when it comes
>> to submodules, given previous threads on this topic) isn't the raison
>> d'etre for your submodule proposal. Quite simply, a syntax that
>> accommodates both grouping lines and grouping files cannot make the latter
>> first class, because the former necessarily requires more ceremony.
>>
>>
>> Okay, but the question still stands: what do we need to make Nevin's
>> use-case first class?
>>
>
> We need to have either *two* spellings for a submodule feature that
> supports both grouping files and grouping lines of code, or we need to have
> *two* features, one for grouping files and another for grouping lines of
> code. In any case, the spelling for grouping files should:
> - Not require braces
> - Not require one file to be a `module` and another to be an `extension`
>
> The simplest such spelling would be:
>
> File A:
> ```
> module Foo
> // rest of file
> ```
>
> File B:
> ```
> module Foo
> // rest of file
> ```
>
>
> This was mentioned earlier, so I will quote my response to the last poster:
>
> That is a valid spelling (Rust, IIRC, allows that spelling), but one that
> is easy to miss sitting in a file and makes it confusing to introduce
> submodules. If you include the annotation then define a submodule later
> down in the file, suddenly you have to remember whether you annotated the
> file or whether the submodule you’ve just written is going into the
> top-level module. See:
>
> // -module-name=Foo
> // module Foo {
> module Bar; // Shorthand for “This file defines Foo.Bar”
>
> /* Code */
>
> // This defines “Foo.Bar.Baz”, but would you know that if it appeared
> below the fold?
> module Baz {}
> //}
>
>
> But that reply was oriented towards a “why can’t we have nested modules
> and a shorthand too?” Here, you’re referring more to the
> one-module-one-file language restrictions, so I will quote another response:
>
> For one-file-per-module, that kind of restriction represents a particular
> way of organizing code and is a design pattern that is supported under this
> proposal. We just happen to not enforce that particular pattern, and feel
> that it is the job of a linter to do so. Really, this kind of restriction
> is to ease the mental burden on compiler writers who use it to build
> compilation unit dependency graphs. Swift already considers all files when
> building its modules because you can extend any type (and now, any module)
> from any one of them so it doesn’t buy us anything other than an arbitrary
> restriction.
>
> Realistically, the only difference between the proposal’s syntax and this
> one is two characters (the braces) and maybe some tabs if you decide to
> enforce that style. There are few objective organizational benefits from
> your style, and it creates a bureaucratic rule where none need exist.
>
I'm sorry, what is the bureaucratic rule?
> Subjectively, of course, people may prefer this way, or they may prefer a
> more ad-hoc approach. But we designed for both cases and showed our hand
> by favoring non-physical organization because it is a subset of possible
> organizational styles available to users.
>
Indeed you did. But you will need to justify why *both* cases are important
to support, and moreover why you would favor the case that's the more
complicated and less often requested, and why the other case should be
burdened with even a single character that's not necessary for its own
support.
To my mind, we’ve offered a syntax and semantics internal to the language
>> that supports file-only aggregation because file-only aggregation enables a
>> subset of the actual use cases of this module system. We aren’t enforcing
>> this by compiler-fiat because it is a stylistic choice that can be enforced
>> by a linter.
>>
>
> It's not enough to offer a syntax and semantics that supports it; if it is
> the intended major use case, the design ought to reflect that by making
> that use case no less cumbersome than necessary.
>
>
> See above for aesthetic concerns.
>
>
> Any variables defined with `internal` access will be visible across those
>>>> files to those extensions and only those extensions (see the section on
>>>> access control and modules). Any variables declared fileprivate or private
>>>> will, obviously, not be visible across these files. As an example:
>>>>
>>>> // FooUtilities.swift
>>>> //
>>>> // -module-name=Foo
>>>> // module Foo {
>>>> // Defines Foo.Utilities
>>>> module Utilities {
>>>> public func exportableOutsideThisSubmodule() {}
>>>> func visibleInThisSubmodule() {}
>>>> private func invisibleToOtherFiles() {}
>>>> }
>>>> //}
>>>>
>>>> // FooUtilities+MoreUtilities.swift
>>>> extension Utilities {
>>>> private func privateHelper() {
>>>> visibleInThisSubmodule()
>>>> }
>>>> }
>>>>
>>>> I’m not sure where you got the impression that we were just trying to
>>>> make another fileprivate happen.
>>>>
>>>>
>>>> Nevin
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170221/5cff9403/attachment.html>
More information about the swift-evolution
mailing list