Making a module optional in a parent module signature

I’m creating a module signature and I would like to make a react component optional. I haven’t found a way yet how to do it? In the example below, I would like to make PageSubtitle optional so if I create a module of the type LibraryType I would not always have to provide it. Does anybody have a solution for this? The rescript docs don’t seem to mention anything about optional modules.

module type LibraryType = {
  module PageTitle: {
    @react.component
    let make: (~title: string) => React.element
  }

  // This should be optional
  module PageSubtitle: {
    @react.component
    let make: (~subtitle: string) => React.element
  }
}

What are you trying to achieve? How would you use such a module?

There are other ways to provide a set of functions, you could use a record for example.

I would like to pass a module Library with react components to a module Menu for instance. If the Library has a PageSubtitle component it will use it if not it will use its own.

But I guess I could use records. Buy I just thought if I could make PageTitle module optional it would be cleaner.

You could also use first class modules and make the pageTitle parameter optional.

Thank you @tsnobip That could be a solution. I’m not sure how it would keep the code more simple.

For now this is what I’m thinking of doing but I would very much welcome any other ideas.

I would like to avoid having to add this line in module Library2 if possible. At the moment, I don’t know how to do it.
module PageSubtitle = PageSubtitle

module type LibraryType = {
  module PageTitle: {
    @react.component
    let make: (~title: string) => React.element
  }

  module PageSubtitle: {
    @react.component
    let make: (~subtitle: string) => React.element
  }
}

module DefaultLibrary: LibraryType = {
  module PageTitle = {
    @react.component
    let make = (~title: string) => {
      <h1 className="text-4xl font-bold"> {React.string(title)} </h1>
    }
  }

  module PageSubtitle = {
    @react.component
    let make = (~subtitle: string) => {
      <h1 className="text-lg font-bold"> {React.string(subtitle)} </h1>
    }
  }
}

module Library1: LibraryType = {
  module PageTitle = {
    @react.component
    let make = (~title: string) => {
      <h1 className="text-2xl font-bold text-primary"> {React.string(title)} </h1>
    }
  }

  module PageSubtitle = {
    @react.component
    let make = (~subtitle: string) => {
      <h1 className="text-xl font-bold"> {React.string(subtitle)} </h1>
    }
  }
}

module Library2: LibraryType = {
  open DefaultLibrary

  module PageTitle = {
    @react.component
    let make = (~title: string) => {
      <h1 className="text-2xl font-bold text-on-background"> {React.string(title)} </h1>
    }
  }

  module PageSubtitle = PageSubtitle
}

module type CustomizableMenuType = (Library: LibraryType) =>
{
  @react.component
  let make: (~menu: Menu.t) => React.element
}

module CustomizableMenu: CustomizableMenuType = (Library: LibraryType) => {
  @react.component
  let make = (~menu: Menu.t) => {
    <div>
      <Library.PageTitle title="Hello World" />
      <Library.PageSubtitle subtitle="This is a very interesting subtitle" />
    </div>
  }
}

module CustomizableMenuInstance1 = CustomizableMenu(Library1)
module CustomizableMenuInstance2 = CustomizableMenu(Library2)

To be honest, I think your solution is the simplest. What’s the problem with this extra line?

Thank you @tsnobip The problem is that a real life case would include at least 30 components and I would need to add many extra boilerplate lines.

It depends how often you want to customize the optional fields, if most of the times you want the default, then using first class modules is likely easier. I don’t have access to my computer until Monday so I can’t write down an example before.

I think I would always be passing a few customised values to the optional fields, but hardly ever all of them. I am curious to see an example using first class modules. I’m not very familiar with their syntax in Rescript and there isn’t much documentation.

1 Like

Here is a demo with a first class modules.

Indeed we should document it.

3 Likes

And here is a demo using a record, it’s likely the most cumbersome of all the variants.

To be honest I’d still use your solution, it’s simpler and more explicit, this way you won’t forget to define custom components when you add new “optional” ones.

Thank you @tsnobip. I think the first example is the best option of all. I wasn’t sure how to turn a function parameter (the value of which is a module/react component) back to a module/react component inside the body of the function. The keyword unpack is what I was after. I couldn’t find any documentation on rescript’s website for it.

1 Like