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

Cyclical imports of modules #627

Open
Goju-Ryu opened this issue Oct 18, 2024 · 6 comments
Open

Cyclical imports of modules #627

Goju-Ryu opened this issue Oct 18, 2024 · 6 comments

Comments

@Goju-Ryu
Copy link
Contributor

Goju-Ryu commented Oct 18, 2024

edit: @Bzero has explained how it is currently possible to have cyclical dependencies in certain situations. Due to how error prone and messy it is likely to become, I think this issue should be more about agreeing on conventions for when we run into this. Which functions go where and what are the new files called and where are they placed?

I am writing some more utility functions for numbat, currently I'm working on str_split which splits a string into a list of strings based on a string pattern. I Would like to put this in the core::strings module as that fits it very well, but because I need the core::lists module and it already has a dependency on the former, this is not possible. I think it would be beneficial to either allow such cyclical dependencies or to document how such cases aught to be handled.

I know F# disallows cyclical dependencies, so if we don't want to support it, maybe we could look to it for other approaches to handle such cases.

@Bzero
Copy link
Contributor

Bzero commented Oct 18, 2024

I stumbled over this topic in numbat once earlier and I think it is actually possible at the moment to have cyclical dependencies of modules on each other as long as the actual functions/function calls in them don't cyclically depend on each other. Whichever module gets imported first will just be ignored the second time the same use statement appears.

That said, cyclical imports may cause a lot of trouble and should be discouraged if not prohibited in my eyes.

@Goju-Ryu
Copy link
Contributor Author

I think it is actually possible at the moment to have cyclical dependencies of modules on each other as long as the actual functions/function calls in them don't cyclically depend on each other.

If so, I couldn't manage to make it work. I was developing a new function, so nothing else depended on it, but depending on module load order either lists or strings module threw an error due to an unrecognized name the first time the respective module used a function from the other.

@Bzero
Copy link
Contributor

Bzero commented Oct 20, 2024

I don't know your exact code of course but I would suspect that you have the use statement before all functions needed by the other module are defined (probably on the very top of the file as we usually do). If you do it "over cross" it should work, e.g:

a.nbt:

# Functions to be used in b.nbt
fn f_a1(x) = x
fn f_a2(x) = x + 1

use b

# Functions using functions from b.nbt
fn f_a3(x) = f_b1(x) * f_b2(x)

b.nbt:

# Functions to be used in a.nbt
fn f_b1(x) = x
fn f_b2(x) = x - 1

use a

# Functions using functions from a.nbt
fn f_b3(x) = f_a1(x) + f_a2(x)

This is quite error prone and not very ergonomic of course which is why I think it should be avoided.

@Goju-Ryu
Copy link
Contributor Author

You are exactly right about how I did it. I see now why it didn't work the way I tried it. I very much agree that this should be avoided. I would rather make a new file than import mid file unless there is very compelling reason for it.

@sharkdp
Copy link
Owner

sharkdp commented Oct 23, 2024

I agree with everything that has been said. If we implement more fine-grained importing and a better module structure, I think we could selectively import certain functions without causing trouble. This works well in languages like Rust, for example. Until then, let's split files and build a tree-like structure without any cycles, if possible.

@Goju-Ryu
Copy link
Contributor Author

I am considering an approach where I add a folder with the name of the file being split. Then the original file can just use the modules in the folder similar to prelude, but each file can be imported independently if desired.

So for example when splitting strings.nbt the following file structure would result:

strings.nbt
strings/
    foo.nbt
    bar.nbt

Where the strings::foo and strings::bar modules are the modules the original strings module were split into.

Would that be an acceptable way to split the files?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants