Hey everyone, I'm new to the language! I came across an interesting article and I'm curious about it. You can find it here: https://thunderseethe.dev/posts/whats-in-a-module/. The article discusses module systems.
in the article's definition, in your opinion, is Roc's module system considered to be a strong module system?
I think roc would be weak, though weak is much better for final performance, so I am not sure strong is truly wanted. For dev builds we have plans that would be much closer to strong for faster compilation without needing to fully compile dependencies. Though I think you would still need to do some very fast scanning of dependencies so it wouldn't be truly strong.
That said, you could see the quick scan as just scanning the interface, so I would argue it is equivalent to strong.
I agree that Roc's module system is weak, and based on the description in the blog post, I think I agree with your assessment. But the blog post says it got the distinction from the Backpack paper, and if I'm understanding right, in the paper, the idea of strong modules is more about the language semantics than the compiler's actual ability to compile modules separately. So I don't think strong modules would need to have a performance impact.
In fact, it looks like the strong module system for Haskell that's proposed in the paper doesn't support separate compilation, only separate type-checking. It's more about the idea that a module can explicitly define the module interfaces it depends on. Similarly to OCaml's functors, you can create "generic" modules that can be instantiated multiple times using different implementations of its dependencies.
Ok. I guess it depends on the exact boundary. Fundamentally if you compile modules fully separate from dependencies you have to do some sort of virtual dispatch over the interface so you lose monomorphization and inlining. Both are very heavy for performance.
5 messages were moved here from #ideas > basic-cli repo default by Brendan Hansknecht.
That argument about separate compilation makes sense to me. But it seems to me like the blog post is a little misleading in that way. I think you can have strong modules without actually compiling modules separately at all.
So I don't think strong modules need to have any effect on runtime performance. With that said, I'm not sure whether it's a good idea for Roc. I really like the idea that it would encourage people to think more about the interfaces exposed by their modules, but my guess is that it might not be worth the complexity.
these names are borderline indefensible - there's a case to be made for reversing them (the design they call "strong" creates weaker coupling between modules) and the implicit value judgment does not seem remotely justifiable
I think better names would be "extern style" modules (where you declare the types of your dependencies, like you do with extern in languages that have that keyword) versus "import style" modules (where you don't)
the "independent compilation" benefit of extern-style is interesting, although I'm not sure how much it matters in practice if you have on-disk caching of partially compiled things
certainly it seems like it could help with clean build times, but the ergonomics cost of having to repeatedly declare the types of every single thing you import seems super high
separately, I don't know why they used the term "namespacing" to refer to information hiding, or "packaging" to refer to compilation units
it almost feels like choosing misleading terminology is a goal or something
I also don't think this conclusion is justified:
It becomes obvious to me, languages want [extern-style] modules.
I'm struggling to find anything positive to say about this article :sweat_smile:
I guess it's interesting to think about the independent compilation piece
A couple of points, because there are 2 very mainstream languages where you have to maintain a parallel abridged version of every file with the declarations in that file, and where that allows embarrasingly parallel compilation (until linking).
These languages are C and C++ :joy:
the "independent compilation" benefit of extern-style is interesting, although I'm not sure how much it matters in practice
In C not so much because C headers are usually very short, but in C++, often, having to parse once and again the same header files actually hinders build performance more than what you get from parallelization. This could obviously be optimized with a much better implementation, the C++ build process is notoriusly old and bad, but still the cost of having to parse those interface files of the dependencies could add up.
This also combines with what Brendan said. You only get inlining if you have visibility of the body of the function you are depending on, so this creates a tradeoff between final runtime speed and compilation speed.
but the ergonomics cost of having to repeatedly declare the types of every single thing you import seems super high
I personally like having a version of other people's code with no implementation details and only the information that matters to me as the user of the code, but maybe it's because I'm just very used to it. For me it's more a plus than not in the UX side, but I am aware that opinions on the topic differ a lot :joy:. Besides, I guess you can sort of emulate this with text editors that collapse the bodies of functions.
it almost feels like choosing misleading terminology is a goal or something
I don't know much about this stuff, but I've been looking a bit at some of the research related to what this article is based on, and I think it makes more sense in the original context. ML modules have been an area of research since the 80s and haven't really made their way into other languages, so it doesn't seem super surprising that the terminologies would have diverged.
certainly it seems like it could help with clean build times, but the ergonomics cost of having to repeatedly declare the types of every single thing you import seems super high
To be fair, I don't think ML modules require you to do this. You only have to declare the type signature for a module you're importing if you actually think it's important. From what I've been reading, the "fully functorized programming style" where you always declare the interfaces you depend on was initially encouraged in the ML community, but it turned out to be way too annoying. I'm not sure how people usually decide what approach to use for a particular module nowadays. Also, since ML doesn't have typeclasses/abilities, you have to use module signatures for that purpose. If Roc added them, I think it would be confusing to decide whether to use modules or abilities for something. Modules are more powerful and probably simpler to understand than typeclasses, but they also look much more clunky and verbose.
My suspicion is that the reason ML/OCaml programmers find it bearable is because people don't usually specify the types in the actual implementation, only in the interface. So a lot of the time, it works out to be just like you do things in Roc, except the type signatures and documentation comments are in MyModule.mli while the actual implementation is in MyModule.ml. But honestly, it still sounds pretty inconvenient to me that the type signatures are in a different file from the implementation. I guess it makes sense that a module system with more academic origins is nicer conceptually but annoying in practice.
So I personally don't think "strong modules" are the right choice for Roc, but I also think they deserve to be taken more seriously than this article might make it seem. I found an online discussion at some point from Elm's early days where Evan Czaplicki was considering adding them to Elm, and at the time, he definitely seemed to prefer them to typeclasses.
Another interesting thing I saw recently: the creator of Rust apparently would have preferred ML-style first-class modules instead of traits:
Traits. I generally don't like traits / typeclasses. To my eyes they're too type-directed, too global, too much like programming and debugging a distributed set of invisible inference rules, and produce too much coupling between libraries in terms of what is or isn't allowed to maintain coherence. I wanted (and got part way into building) a first class module system in the ML tradition. Many team members objected because these systems are more verbose, often painfully so, and I lost the argument. But I still don't like the result, and I would probably have backed the experiment out "if I'd been BDFL".
Last updated: Jun 16 2026 at 16:19 UTC