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

BadSetAxis exception on the same element being added with and without an activation (InconsistentSet error in compile-time check)

neko-kai opened this issue · comments

commented

From Telegram [RU], reproduction:

import distage._
import cats.effect.IO
import izumi.distage.framework.CoreCheckableAppSimple

trait PonvScenario[F[_]]
trait LogMessages[F[_]] extends PonvScenario[F]

object App extends CoreCheckableAppSimple[IO] with App {
  def a[F[_]: TagK] = new ModuleDef {
    make[LogMessages[F]]
    many[PonvScenario[F]].ref[LogMessages[F]]
  }
  
  def b[F[_]: TagK] = new ModuleDef {
   make[LogMessages[F]].tagged(Mode.Test)
   many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Test)
  }
  
  def program[F[_]: TagK] = a[F] ++ b[F]
  def module: ModuleBase = program[IO]

  def roots: Roots = Roots.Everything
  // BadSetAxis
  Injector[IO]().produce(program[IO], Roots.Everything, Activation(Mode -> Mode.Test)).use {
     _ => IO.unit
  }.unsafeRunSync()
}

object Test {
  import izumi.distage.framework.PlanCheck

  // InconsistentSetMembers
  object WiringCheck  extends PlanCheck.Main(App)
}

Scastie: https://scastie.scala-lang.org/GA5bSD3xRmORHidCmeTyNQ

commented

Another instance:

import distage._
import cats.effect.IO

trait PonvScenario[F[_]]
trait LogMessages[F[_]] extends PonvScenario[F]
trait F[A]

object App extends App {
  val a = new ModuleDef {
    make[LogMessages[F]]
    many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Prod)
  }
  val b = new ModuleDef {
   many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Test)
  }
  val program = a ++ b
  Injector[IO]().produceRun(program, Activation(Mode -> Mode.Test)) {
     x: Set[PonvScenario[IO]] => IO.unit
  }.unsafeRunSync()
}

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

Note that here both elements are tagged instead of one, so they should probably be mutually exclusive...

First example is not a bug. make[LogMessages[F]] should be erased in favour of many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Test) thus .ref[LogMessages[F]] gets invalidated.
It looks weird but it's a sound behavior. We may try to improve UX for such cases by either improving diagnostics or changing our approach to set element filtering. Latter can't be done without a very detailed proposal considering a lot of different cornercases.

Second example indeed demonstrates unsoundness though I believe that the real problem is that both definitions

many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Prod)
many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Test)

are merged into

many[PonvScenario[F]].ref[LogMessages[F]].tagged(Mode.Prod, Mode.Test)

We may either improve merging logic (e.g. never merge conflicting axis) or remove merging completely. Seems like we don't need it anymore with our new solver.