Get around inline optimization

Inline optimization is a cool feature, but when you develop a library it may unpredictably increase output size.

I have a functor that creates a module with a big function. The functor is used multiple times in the same module where it’s defined.
The problem is that rescript compiler creates a separate function for every module with inline functor args. I’ve made a playground to better showcase the problem:
https://rescript-lang.org/try?code=LYewJgrgNgpgBAWQIYGsYGVgBcAWcC8cAFAFBxwAiMWSAllAM4BccA3meXLFnCrQHZgWDLACcBAcw4BfADQkAlAQB8bDtzgAjWhIBiEfgGMstEPwLEl+Ve05wAUgwB0UEBKIAiAJLAkEgfBIXCA8IABmcIbg8AIiMEhgcOFwuLQMDs6uEh4KHORUNPTOfIIyJNIkJKCQsHAAwkg8hMhomLhEtlzUvAKJhB6GjR7luVXg0PAUbhYtGNg4HerdJX1wHmBuw9K5QA

When using a functor from another file-module it’s just a function call - the behavior I want. But I can’t move it to another file-module because it depends on the types from the first.

Do you have any suggestions/hacks on what to do in the situation?

I’m not aware of any technique to influence the compiler, but would you be able to move the big function to its own module and pass the values you need as arguments with generic types? For example:

module Kind = {
  let bigFunction = (make: () => 'a, use: 'a => string) => {
    Js.log("Imagine a lot of code instead of this Js.log")
    make()->use
  }
}

module MakeSmth = (
  Details: {
    type t
    let make: () => t
    let use: t => string
  },
) => {
  let bigFunction = () => Kind.bigFunction(Details.make, Details.use)
}

module Cat = MakeSmth({
  type t = string
  let make = () => "cat"
  let use = value => value
})

module Dog = MakeSmth({
  type t = int
  let make = () => 123
  let use = value => Belt.Int.toString(value)
})

The problem is that functor still requires the types from the module where it’s used that causes dependencies cycle. I’m thinking to move all the shared types to another file-module to resolve this, but I wasn’t able to do it after the first try. Although it looks like it’s the right way to go.

I never used this functionality of the language before, and not sure what the downsides might be, but it seems that if you use a regular function with module/unpack instead of a functor, the inlining does not occur:

https://rescript-lang.org/try?code=LYewJgrgNgpgBAFwJ4Ad4BEYIIYEsoDOAKqvALxwDeAUHHLAnANa4B2YAXHAQgE5sBzagF9q1UJFiJScAMrAEACxJo4FGnQZwARrgEAxCKwDGCXCFZcjuRmQB83PoJFitwbExjylauAAowLDxCLgloGD9MHHxiUgBKONDwcL9vZXi1Bw04MNg-WjoqAsKc5Kko4IJfIxRsYyYAoJi44sKtXQMjU3NWXz84zKKSkoApAgA6KBABPwAiAEl3ATZ4bHoQRhAAMzhjcHg2HhhsMDhtxEVcKrHJ6dmW4cKKmPGWdla6URKvuBbRcTK8AAwthbHAanUGgV3J40vkSrkIh8ho96FhmGxThRZsZQbNkT86HEADQFFotAGSDDTaqsWr1eE5DxeBSKRl0RHswrZR5aN5YuCzMB3AmtElk6gtIA

module type DetailsType = {
  let kind: string
}

module type SmthType = {
  let bigFunction: unit => string
}

let makeSmth = (details: module(DetailsType)): module(SmthType) => {
  module(
    {
      module Details = unpack(details)
      let bigFunction = () => {
        Js.log("Imagine a lot of code instead of this Js.log")
        Details.kind
      }
    }
  )
}

module Cat = unpack(
  makeSmth(
    module(
      {
        let kind = "cat"
      }
    ),
  )
)

module Dog = unpack(
  makeSmth(
    module(
      {
        let kind = "dog"
      }
    ),
  )
)
1 Like