7mind / izumi

Productivity-oriented collection of lightweight fancy stuff for Scala toolchain

Home Page:https://izumi.7mind.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

include(module) from mutiple modules leads to ConflictResolutionException

Ravenow opened this issue · comments

  class CoreModule() extends ConfigModuleDef {
    make[AppConfig].from(CoreModule.unsafeConfig)
  }

  class Configs() extends ConfigModuleDef {
    include(CoreModule())
    makeConfig[ApplicationConfig]("application")
  }

  class S1() extends ModuleDef {
    include(new Configs())
    make[Service1]
  }

  class S2() extends ModuleDef {
    include(new Configs())
    make[Service2]
  }

  class AppModule() extends ModuleDef {
    include(new S1())
    include(new S2())

    make[ServiceApp]
  }

such modules chain will lead to ConflictResolutionException when you try to create ServiceApp, mutiple applicationConfigs are here

Sample code here:
https://github.com/Ravenow/distage-sample

commented

@Ravenow
Workaround: make bindings in Configs() module unique by using an object:

object Configs extends Configs()

And including using include(Configs) instead of include(new Configs())

https://scastie.scala-lang.org/syIPLY28RpqVVXXX98JjWw

Another workaround

Use distage-plugins instead of hierarchical module includes - https://izumi.7mind.io/distage/distage-framework.html#plugins

Plugin machinery guarantees that every module is only instantiated once, so all bindings are unique.

Explanation

Why does that happen?

We have hacks to consider class constructor bindings to always be equal to each other, so you can have multiple duplicate bindings for the same class constructor:

def x = new ModuleDef { make[A] }

assert((x ++ x) == x) // true

This does not apply to makeConfig.

Why not? Because you can use custom pureconfig codecs or have different implicit scopes. There's no exact guarantee that two makeConfig[X] bindings will use the same pureconfig instance.

However, there is probably a way to do this properly, it's just something that was left for later.

Thanks for explanation. Making config modules as objects helps.