chansey97
2021-10-21 10:25:51

Is there any conventional way to work around mutual references in modules? I know that Racket does not support mutual references in modules, but sometimes it will happen. I mean that possibly some big module can be conceptually divided into two parts (without overlapping), so that we can write in two files. N.B. I found that racket/include seems a good way, but I haven’t seen many people use (?). Is there any drawback of racket/include? Another way is to extract common interfaces, but I don’t want to refactor too early… Thanks.


soegaard2
2021-10-21 11:56:54

I sometimes collect parameter definitions and/or structure definitions in a single file. Then the other modules can require that file.


soegaard2
2021-10-21 12:00:20

If that’s not enough (let’s say two one module A needs a value from B, but a direct require makes a circular reference) then I make a parameter holding the value. Its initial value is #f, but when module B is instantiated, it will set the parameter to the shared value.


arifshaikh.astro
2021-10-21 12:01:17

What is the easiest way to generate a list from start to stop with step ?


soegaard2
2021-10-21 12:01:28

The above approach suits me - but an alternative is to use units.


rokitna
2021-10-21 12:12:01

I’ve thought about using racket/include for this too, actually. It seems like a quick low-tech way to get something working. I can imagine possible drawbacks, like whether it works okay with DrRacket background expansion and whether source locations are reported in useful way, but I haven’t tried it yet.

If racket/include didn’t exist, making a file that’s just one big macro definition and a bunch of requires might be a similarly workable approach (and its drawbacks might be similar in nature).


rokitna
2021-10-21 12:29:08

Most of my ideas around this really do boil down to quoting everything in a file. There are two reasons for this that seem pretty essential:

• Only one of the files’ compilation results is going to be able to contain the compilation of the mutually recursive parts anyway, so the rest of the files need to supply their parts in quoted form so that the compiling one has code to compile. • If one file defines a syntax that should be visible in the others, then the others can’t be fully compiled independently of it. So, breaking up an N-cycle into (N – 1) modules of quoted code and 1 module that compiles them all seems like the simplest starting point, and a racket/include approach fits the bill.


rokitna
2021-10-21 12:36:19

The approach I propose at https://github.com/racket/rhombus-brainstorming/issues/166\|https://github.com/racket/rhombus-brainstorming/issues/166 is basically that plus a few things to make it more seamless: The guests (quoted ones) can carry some metadata about where to find their host, so that a special require variant can appear to import things from the guest directly (while actually following the guest’s redirect). The modules contain declarations that determine their own status as a host or a guest. The guests’ self-quoting and the hosts’ guest-fetching happen as an implicit result of those declarations, powered by #lang-level support for the declarations.


rokitna
2021-10-21 12:46:46

A more sophisticated approach might cut down on some of the self-quoting, allowing certain non-mutually-recursive and non-syntax-dependent parts of files to be compiled during the compilation of the guests. I think the easiest design for this would require explicitly specifying which code can be compiled early like this. A higher-tech approach might be able to do static analysis for this. For the short term, a low-tech approach would be simply to put these parts in another module.



rokitna
2021-10-21 12:51:47

There’s also inclusive-range if you want stop to be inclusive. :)


samth
2021-10-21 13:25:22

I strongly recommend against include. It will break lots of things — everything from separate compilation to IDE tools to macro hygiene.


samth
2021-10-21 13:27:09

The usual approaches for this are various forms of mutation. Here are three ways to do it: 1. Have one module export a mutator to allow setting the appropriate value. 2. Have one module export a parameter to allow setting the apporopirate value, and then reference everything via the parameter. 3. Use units.


arifshaikh.astro
2021-10-21 13:39:49

great! exactly what I was looking for


chansey97
2021-10-21 14:36:45

Thanks all!


chansey97
2021-10-21 14:37:38

Hope Racket could support module mutual references in the future.


samth
2021-10-21 14:38:35

Unfortunately macros and mutually-recursive modules are fundamentally incompatible.


notjack
2021-10-21 21:25:49

@samth what about having multi-file modules then? that’s basically what rust does, for example


rokitna
2021-10-21 21:30:04

That’s what I was about to say, but in fewer words. :)


notjack
2021-10-21 21:33:00

yeah I think saying “pick two: separate compilation, cross-file cyclic references, and macros” is totally fine if you can opt-in to making compilation units larger than one file when you need to


chansey97
2021-10-22 03:00:35

@notjack I haven’t used Rust yet. Does it (multi-file modules) work like C# partial class? Sounds good to me.


notjack
2021-10-22 03:10:42

@chansey97 In Rust, the compilation unit is an entire crate. Rust does not compile files separately, it only compiles crates separately, and if you change a single file you have to recompile the whole crate.


notjack
2021-10-22 03:12:14

(“crate” in Rust means roughly the same thing as “package” in Racket)