> On Feb 22, 2017, at 12:41 AM, Matthew Johnson <matt...@anandabits.com> wrote:
> 
> 
> 
> Sent from my iPad
> 
> On Feb 21, 2017, at 11:09 PM, Robert Widmann <devteam.cod...@gmail.com 
> <mailto:devteam.cod...@gmail.com>> wrote:
> 
>> 
>>> On Feb 21, 2017, at 11:59 PM, Matthew Johnson <matt...@anandabits.com 
>>> <mailto:matt...@anandabits.com>> wrote:
>>> 
>>> 
>>>> On Feb 21, 2017, at 10:41 PM, Robert Widmann <devteam.cod...@gmail.com 
>>>> <mailto:devteam.cod...@gmail.com>> wrote:
>>>> 
>>>> By API boundaries I mean both the one internal to MyModule.Foo and the one 
>>>> defined by MyModule.  Here “the API boundary” is explicitly about the 
>>>> submodule MyModule.Foo, whose internal state may have been “unsealed” in 
>>>> the top level by the extension, but has not been re-exported.
>>> 
>>> I’m sorry, but I just don’t understand how modules form an API boundary in 
>>> this system.  To me a boundary means something that blocks access.  In this 
>>> system `internal` ranges over the entire module and all submodules.  The 
>>> only boundaries I can see besides the module itself are files and lexical 
>>> scopes (with `fileprivate` and `private`).  
>>> 
>> 
>> A module is a named region that introduces a lexical scope into which 
>> declarations may be nested. The name of the module can be used to access 
>> these member declarations. A module, like other aggregate structures in 
>> Swift, may be extended with new declarations over one or more translation 
>> units (files).
>> 
>> 
>> Your API boundary lives, as it does today, at the edges of each (sub)module 
>> declaration.  APIs that are public or open in a module defines code that is 
>> free to move across this boundary and into the open.  APIs that are internal 
>> are free to have their modules unsealed into other internal modules to 
>> enable modular composition.  APIs that are private and fileprivate do not 
>> participate in the API boundary because they are not eligible for any kind 
>> of export.  
>> 
>> If any of that is unclear, please let me know. 
> 
> Yes, in fact parts are unclear.
> 
> "APIs that are public or open in a module defines code that is free to move 
> across this boundary and into the open"
> 
> This is unclear because you're saying submodules form an API boundary and 
> you're also saying we need to make APIs open or public to allow them to move 
> across this boundary.  But then you say we can unseal it (import or extend, 
> right?) within the module and gain visibility to the internal symbols.  Are 
> you trying to say that it's a soft boundary within the module that can be 
> permeated with an import or by extension?

Of course.  Soft implies more permeability than you are actually afforded, but 
if you want to think of it that way then it may help to put it in context.  

For what it’s worth, the bulk of the discussion around this feature is focused 
on author-side concerns like the behavior of internal modules because access 
control makes a mess of any reasonable semantics. 

> 
> If so, that's not the kind of boundary I think many of us are talking about.  
> We're talking about a hard boundary within the module, but broader than a 
> file.

How then, does one go about accessing declarations contained in these kinds of 
impermeable modules?  You must define points of exposure to be able to use the 
module.  What you’re describing is as though you had can only build hierarchies 
of completely private types and then cherry-pick them one-by-one into the open 
- which, mind you, is not a pattern encouraged by any of the access control 
levels we have today and isn’t supported by any language I’m aware of.

> 
>>>  means that it is trivial to put code anywhere within the module that 
>>> extends the submodule and wraps a symbol in a new name and declares it 
>>> `public`.  
>> 
>> Precisely.  That’s the same pattern that good Swift code, arguably good code 
>> in any language that enables hiding, uses today.
>> 
>>> They can also trivially add a `public import MyModule.Foo` anywhere at the 
>>> top level of their file because every file is forced to include top level 
>>> scope.
>> 
>> Perhaps you misunderstand.  Say the APIs in MyModule.Foo were all of 
>> internal or stricter access: The re-export is a no-op.  You cannot change 
>> the access level of declarations, you can only do the modular thing and wrap 
>> them in a palatable interface for export by a module you want to be 
>> user-facing.  You have to decide to make an API public, just as today you 
>> have to decide to make part of an interface public.  I don’t see how this is 
>> distinct from the goals of this proposal.
> 
> Yes, I understand this.  But submodules aren't visible outside the module by 
> default.  It's possible for a submodule to have public and open symbols 
> without the top level public import anywhere in the program.  

> What I'm saying here is that someone in a distant part of the code base could 
> arbitrarily add it if they decided to.  The system doesn't prevent it.  
> 

Because, by definition, you are not in a “distant part of the codebase” when 
you are extending a module.  You’re introducing related functionality under the 
same namespace with more related functionality.  Anything else is fundamentally 
anti-modular because it pollutes different concerns together into an 
interlocking directorate.  Miles away implies cognitive and semantic distance 
when you’re probably physically in the same directory!

> I understand that you consider that a non goal.  I'm simply pointing out that 
> the system has this property.  I think it's reasonable to want a system with 
> different properties.  And I don't think it's clear yet exactly what kind of 
> system might garner the support necessary to be accepted as Swift's submodule 
> system.  That's part of the reason we have these discussions! :)

I so appreciate this, too.  I genuinely enjoy discussions that try to poke 
holes and prod out better explanations. It’s how you iterate on proposals and 
just make good things happen in a community like this.

~Robert Widmann

> 
>> 
>>> 
>>> In my opinion, we need to identify what goals we have for a submodule 
>>> system - what problems are we trying to solve and what use cases do we 
>>> intend to enable.  
>>> 
>>> There are quite a few of us who want the ability to form solid API 
>>> boundaries inside a module and view this as one of the fundamental features 
>>> of a submodule system.  It’s reasonable to ask why we view this capability 
>>> as essential.
>>> 
>>> I can’t speak for anyone else, but here are a few reasons why it’s 
>>> important to me:
>>> 
>>> * Solid API boundaries are essential to good design.  
>>> * Having access to an entire code base does not reduce the benefits of #1.  
>>> Some code bases are substantial in size and hard boundaries are important 
>>> to keeping them manageable.
>>> * Using full-fledged modules to do this is possible, but also involves a 
>>> bit of ceremony that is incidental, not essential complexity in many cases. 
>>>  It would be better to have a lighter weight mechanism to do this.
>>> * Swift currently only has whole module optimization, not whole program 
>>> optimization.  There is a performance penalty to using full-fledged modules.
>>> 
>>>> 
>>>> ~Robert Widmann
>>>> 
>>>>> On Feb 21, 2017, at 11:38 PM, Matthew Johnson <matt...@anandabits.com 
>>>>> <mailto:matt...@anandabits.com>> wrote:
>>>>> 
>>>>> 
>>>>>> On Feb 21, 2017, at 10:29 PM, Robert Widmann <devteam.cod...@gmail.com 
>>>>>> <mailto:devteam.cod...@gmail.com>> wrote:
>>>>>> 
>>>>>> This level of access, the “private to this submodule except to the 
>>>>>> select set of interfaces I want to see it” level, is the equivalent of 
>>>>>> friend classes in C++.  I don’t consider leaving this out to be a hole, 
>>>>>> nor is it an "encapsulation-related problem” because at no point can you 
>>>>>> break the API boundary and re-export anything here with a higher level 
>>>>>> of access than it had previously.
>>>>> 
>>>>> By API boundary you mean the top-level module, right?
>>>>> 
>>>>>> 
>>>>>>> On Feb 21, 2017, at 11:13 PM, Matthew Johnson <matt...@anandabits.com 
>>>>>>> <mailto:matt...@anandabits.com>> wrote:
>>>>>>> 
>>>>>>> 
>>>>>>>> On Feb 21, 2017, at 10:11 PM, Matthew Johnson <matt...@anandabits.com 
>>>>>>>> <mailto:matt...@anandabits.com>> wrote:
>>>>>>>> 
>>>>>>>> 
>>>>>>>>> On Feb 21, 2017, at 9:47 PM, Brent Royal-Gordon via swift-evolution 
>>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>>>>>>> 
>>>>>>>>>> On Feb 21, 2017, at 7:38 PM, Robert Widmann 
>>>>>>>>>> <devteam.cod...@gmail.com <mailto:devteam.cod...@gmail.com>> wrote:
>>>>>>>>>> 
>>>>>>>>>> Correct.  Because, in dividing the submodule across an extension, 
>>>>>>>>>> you have placed what should be a private API into a 
>>>>>>>>>> differently-scoped location.
>>>>>>>>> 
>>>>>>>>> Okay. So is your submodule design not intended to address the "I want 
>>>>>>>>> to encapsulate implementation details so they're only visible to 
>>>>>>>>> several units of code in different files, but not the entire module" 
>>>>>>>>> use case? Because if there's no way to scope a symbol to "everything 
>>>>>>>>> inside this submodule, but nothing outside this submodule", I think 
>>>>>>>>> it leaves that use case unserved.
>>>>>>>> 
>>>>>>>> Unless I’m missing something there is also another 
>>>>>>>> encapsulation-related problem with the proposed design.  Let’s suppose 
>>>>>>>> for the sake of discussion there was a `submoduleprivate` access 
>>>>>>>> modifier (intentionally ungainly and not realistic).
>>>>>>>> 
>>>>>>>> // File 1
>>>>>>>> module Foo {
>>>>>>>> // internal, visible to the whole module
>>>>>>>> class Bar { submoduleprivate var protectedState: Int = 0 }
>>>>>>>> }
>>>>>>>> 
>>>>>>>> // File 2 - Has nothing to do with Foo at all
>>>>>>>> import MyModule.Foo
>>>>>>>> 
>>>>>>>> module NotFoo {
>>>>>>>> // Hey, I need to see Bar.protectedState!!!
>>>>>>>> func totallyNotFoo() {
>>>>>>>>   var bar = Bar()
>>>>>>>>   bar.foosExposedPrivates = 42
>>>>>>>> }
>>>>>>>> }
>>>>>>>> 
>>>>>>>> // ok, I’ll just add an extension to Foo so I can see submoduleprivate 
>>>>>>>> and wrap what I need
>>>>>>>> module Foo {
>>>>>>> 
>>>>>>> Oops, this should have been `extension Foo`, but otherwise I believe it 
>>>>>>> is valid under this proposal.
>>>>>>> 
>>>>>>>> // Hey, I’ll be nice and keep it fileprivate, but I could make it 
>>>>>>>> public if I wanted to.
>>>>>>>> extension Foo {
>>>>>>>>     fileprivate var foosExposedPrivates: Int {
>>>>>>>>        // Yep, I’m inside Foo so I can see it’s submoduleprivate stuff
>>>>>>>>        get { return protectedState }
>>>>>>>>        set  { protectedState = newValue }
>>>>>>>>     }
>>>>>>>> }
>>>>>>>> }
>>>>>>>> 
>>>>>>>>> 
>>>>>>>>> -- 
>>>>>>>>> Brent Royal-Gordon
>>>>>>>>> Architechies
>>>>>>>>> 
>>>>>>>>> _______________________________________________
>>>>>>>>> swift-evolution mailing list
>>>>>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>>>> 
>>>>>>> 
>>>>>> 
>>>>> 
>>>> 
>>> 
>> 

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

Reply via email to