Module thoughts 2017-07-13

Lang team meeting: modules

Insight #1: what visibility means

  • system today can be seen as incoherent; could be relaxed
  • existing goal:
  • if you see a def’n, just looking at the visibility declaration gives you an “upper bound” on what code could access that def’n
  • therefore, re-exporting an item with larger visibility violates this
  • aturon thinks annotation should tell you:
  • what code you need to check to figure out more about this definition
  • in any possible world, we have to at least examine all the code in this scope to know how it’s being used
  • from that POV, re-exporting is harmless
  • more concretely:
  • if I see an item has pub(crate) visibility, to understand today how it is being used, I have to look at all the code in the crate (in principle)
  • if we allowed that item to be re-exported as “world public”, that re-export must occur somewhere in the crate
  • similarly:
  • something declared private would have to be re-exported within the module that defined it
  • if you have a type definition fields and methods and what not
  • all of those are provided at some level of visibility (at some module in the crate)
  • anywhere within that scope of visibility, you could wrap that type in a newtype, re-exposing its fields/methods through delegation/accessors, as public
  • just “a way” that the item could be used
  • I (aturon) don’t think the public/private restriction is buying us that much
  • some objections:
  • when writing (e.g.) unsafe code, it is nice to look at def’n, and easily know this is a “trusted type” where you can plausibly control all uses — i.e., you know that the code intends to defend an invariant — without having to search for whether it is re-exported
  • similarly, when annotating whether something is part of public API
  • maybe affects unsafe code rules too? because if something is re-exported, that may export the API
  • part of what makes things complicated:
  • items being defined in a different place than where it wants to be exposed
  • we’ve been talking about it in terms of “facades” — but applying that label makes it sound more exotic, even though it is ubiquitous in some form

Insight #2: breaking down facade use-cases

  • lots of private modules, but very flat public API
  • very common pattern; here, internal module structure is being used for privacy and files
  • less commonly, there are places where the internal structure is more meaningful
  • possible example: sys in libstd
  • not really a facade though
  • nmatsakis had a bit of a hard time grasping the distinction 😃 
  • but it seems to be to some extent about whether other parts of your code want to name it at the distinct path

Proposal

  • Directories represent meaningful module hierarchy
  • Files are “anonymous” - the directory declares a new module, files are not their own modules (but create a privacy boundary)
  • If we relax the priv-in-pub rule, you can facade by declaring an item pub(crate) and then re-export it as pub.

How to compare to today’s system

  • main case that changes meaning:
  • you would not do mod foo { pub fn bar() }, but rather pub(crate) fn bar()
  • plus we can re-export to greater visibility
  • if you were to do mod foo { pub fn bar() }, you would get the foo as pub 

Notes during meeting

  • Yet Another Iteration =)
  • ... see above …
  • aturon: maybe it’s ok to just make pub(crate) ergonomic and not worry so much about more complex stuff?