Need some advice on structuring code and files...yeah it's about circular dependencies, debug logging, util functions

So I’m developing a compoment and unfortunately I’m debuging it using simple Js.log everywhere :-\

The problem is that logging complex types are not really nice to look at.

So first question is if there is any way to log a rescript value besides just showing it’s Js representation?

The second question is about separating modules into multiple files.

I wanted to create a file “MyModule_Utils.res” to put all the debug logging auxiliary function.

The problem is that they need to know the types from MyModule and MyModule needs to import those helper functions.

Can someone give me some advice on this?

Having to put everything in the same file or else having to put everything on separate files is kind’of a bummer :frowning:

1 Like

You probably want 3 files:

  1. Model.res which only contains types and operations on those types.
  2. IO.res which contain side-effects on types in Model.res,
  3. Component.res? which uses types from Model.res and debug utils from IO.res

If you can break through the mental barrier of having everything in a single file, then you could replicate the above design within the same file too.

1 Like

You should try to avoid circular module dependencies…it gets messy fast. Is there any reason not to put your utility functions in the same module as the type they work on?

jayeshbhoot gave you one way to avoid it, but if you absolutely have to have your MyModule_Utils (which needs access to types in MyModule) in a separate module while also being able to call those utils from the original MyModule module, you will have to break the circular dependency somehow. A couple of ways you could do it are with functors or first-class modules.

Here’s a basic skeleton of the functor solution. Let’s assume your module is called Person:


PersonIntf.res

Here is where any module types or other types that you need to share will live. (Note: Intf stands for interface, S for signature.)

module type S = {
  type t = ...
}

PersonUtils.res

Here is where your utility functions will live. This example is for “showing” the type (i.e., convert to string). It uses a functor to create a module parameterized by PersonIntf.S. Note that it doesn’t depend directly on Person.res, rather PersonIntf.res

module ShowPerson = (Person: PersonIntf.S) => {
  let show: Person.t => string = person => ...
}

Person.res

Here is your non-utility Person code. Note the inner module T. It is there so we can use it with the ShowPerson module.

  • the include PersonUtils.ShowPerson(T) gives you access to the show functions
  • the include(T): it gives you direct access to the stuff in T directly in Person
module T = {
  type t = ...
}

include PersonUtils.ShowPerson(T)
include(T)

/// more stuff...

So now you have a chain rather than a circle: Person <= PersonUtils <= PersonIntf. Depending on your needs, you may need to include PersonIntf in Person as well, but that is still okay.


(If you’re worried about how this will affect your generated JS code…use the un-curried functions to get pretty good compiled JS.)

Again, I don’t think it’s the best idea to do this, but you could.

2 Likes

Hi. Thanks a lot for the responses and please apologize for my late response.
I’m quite a newbee (still haven’t even uses functors) so it takes a while for me to even process the responses.
Hope the question is relevante enough and that the answers could help other thought.
Thanks again :slight_smile:

1 Like