Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use/import a specific module? #13915

Closed
BryantLam opened this issue Aug 29, 2019 · 6 comments
Closed

How to use/import a specific module? #13915

BryantLam opened this issue Aug 29, 2019 · 6 comments

Comments

@BryantLam
Copy link

BryantLam commented Aug 29, 2019

This issue asks if there should be syntax to differentiate use/import of modules at different places in the module hierarchy (e.g. root, submodule, sibling of parent module, ...). Such a feature could resolve certain unintended behaviors such as when a module with a conflicting name is added to a library (described in #14014 (comment) ).


Summary of Problem

Follow-on to #12923, #13524, #13536 (comment), #11262

I need a way to disambiguate a use or import statement when the module hierarchy and viable search targets look like this:

module M {
// mason package main module
  module M {
    // parent
    module M {
      // current module
      module M {
        // child
      }

      import <?>; // What do I put here?
    }
  }
}

and

$ tree $CHPL_HOME/modules/standard
standard
├── M.chpl
├── M
│   └── L.chpl
├── S.chpl
...

Please cover all cases so that I can import S where S is a sibling of M and import L where L is a child of M, for each M = grandparent, parent, itself (if I wanted to rename myself inside M due to a conflict like this example), child, grandchild, this Mason package's main module, and other modules in the module path.

I believe these are the solutions in Rust (todo: verify):

// S
use super::super::super::S; // sibling of grandparent
use super::super::S; // sibling of parent
use super::S; // sibling of current module
use self::S; // sibling of child; i.e, S is a submodule to current module
use self::M::S; // sibling of grandchild; S is a submodule of child

// N/A: Sibling of this package's main module can't exist in Rust?
use extern::S; // some dependency S in Cargo.toml or module search path; probably standard/S.chpl

// L
use super::super::L; // child of grandparent
use super::L; // child of parent; sibling
use self::L; // child of current module
use self::M::L; // child of child
use self::M::M::L; // child of grandchild

use crate::L; // child of this package's main module
use extern::M::L; // child of some dependency M in Cargo.toml or module search path

To kick off some discussion with a notional filesystem-based syntax:

// L
import ../../L; // child of grandparent
import ../L; // child of parent; sibling
import ./L; // child of current module
import ./M/L; // child of child
import ./M/M/L; // child of grandchild

import package/../S; // sibling of this package's main module*
import package/L; // child of this package's main module
import /M/L; // child of some dependency M in Cargo.toml or module search path

and so on. This analogy breaks down pretty quickly when actually using the dot:
import package(dot)..(dot)S

* Not sure what to do with this, especially due to #13524 (comment). Is a package a module? Is package a keyword to wrap a set of modules?

When just doing import M, the behavior would be a combination of: look for submodules, then look through the module search path. I don't think there's any concerns with that since it follows the intent of #12923.

@BryantLam BryantLam changed the title How to use/export a specific module? How to use/import a specific module? Aug 29, 2019
@lydia-duncan
Copy link
Member

I might get to this today if someone doesn't get to it first.

Incidentally, I'm uncertain if it would be worth adding the answer to the modules primer, since there seems to be a lot more pieces than would normally be covered

@lydia-duncan
Copy link
Member

Okay, this is what I think you're asking:

module M {
  module M {
    module M {
      module M {
        module S {
          proc foo() {
            writeln("A");
          }
        }
        import < >; // To get each of the specific foos
        foo();
      } // M.M.M.M
      
      module S {
        proc foo() {
          writeln("B");
        }
      }
    } // M.M.M
    
    module S {
      proc foo() {
        writeln("C");
      }
    }
  } // M.M
  module S {
    proc foo() {
      writeln("D");
    }
  }
} // M

We do not have a way to disambiguate S from that particular location today. We search up scopes for module name and go with the first best answer - given that there is no "unique" path (from the perspective of naming) to each of the S's that are further away, you'll first find the match in the closest scope - M.S will find M4 before M1-3 and so will get the foo that prints A; M.M.S will find the relationship between M3 and M4 before the other pairs, etc. I believe this means that today only an import in M1 is capable of referencing all four S modules, because there is no duplication in what M.S can mean.

W.r.t the crate references, we similarly do not have a strategy to do what you are asking.

I think it would be reasonable to open feature requests for both something like super.M and crate.M

@BryantLam
Copy link
Author

I agree. It is important that import M has sane defaults as a combination of the more specific path-disambiguation approach that this issue raises.

There's a slightly overlapping issue of how a keyword like root or package/brick would actually work because straightforwardly searching the module path might not be the correct solution. I mentioned parsing the Mason.toml file as a way to get a project's dependencies; the reality is that the compiler may need a flag to indicate how to find a dependency so that mason can give them to chpl.

@BryantLam
Copy link
Author

Discussion of proposed additional disambiguation syntax from Rust: The Great Module Adventure Continues. Rust has a similar problem to Chapel's import M where — depending on scope — it's using a module that the user didn't expect or not finding a module that the user would expect. This link focuses on syntax for more disambiguation.

What does Python do for each of these cases?

@bradcray
Copy link
Member

@lydia-duncan / @BryantLam / @mppf: Can this be closed now with the new support for this. and super. for modules?

@mppf
Copy link
Member

mppf commented Apr 24, 2020

Yes, the story with import is relatively clear:

  • import A always imports a top-level module
  • import this.A always imports a submodule
  • import super.A always imports a sibling module

@mppf mppf closed this as completed Apr 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants