Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layer overwrite possible bug #8693

Open
adrian-salajan opened this issue Mar 15, 2024 · 2 comments
Open

Layer overwrite possible bug #8693

adrian-salajan opened this issue Mar 15, 2024 · 2 comments

Comments

@adrian-salajan
Copy link

I'm not sure if this is expected behavior or I am missing something about how ZLayers work.

Below code prints:

bbbbb
bbbbb

I would expect it to print:

aaaaa
bbbbb

but somehow the 2nd layer overwrite the first ones, even though they are injected separately

object LayerTest extends ZIOAppDefault {

  case class WrapperA(printer: Printer) {
    def print:UIO[Unit] = printer.print
  }

  object WrapperA {
    val layer = ZLayer.fromFunction(WrapperA(_))
  }

  case class WrapperB(printer: Printer) {
    def print: UIO[Unit] = printer.print
  }

  object WrapperB {
    val layer = ZLayer.fromFunction(WrapperB(_))
  }

  trait Printer {
    def print: UIO[Unit]
  }

  case class PrinterLive(string: String) extends Printer {
    override def print: UIO[Unit] = ZIO.succeed(println(string))
  }

  object PrinterLive {
    val layer: ZLayer[String, Nothing, Printer] = ZLayer.fromFunction(PrinterLive(_))
  }


  val stringLayerA = ZLayer.succeed("aaaaa")
  val stringLayerB = ZLayer.succeed("bbbbb")

  override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = (for {
    a <- ZIO.service[WrapperA]
    b <- ZIO.service[WrapperB]
    _ <- a.print
    _ <- b.print
  } yield ())
    .provide(
      stringLayerA >>> PrinterLive.layer >>> WrapperA.layer,
      stringLayerB >>> PrinterLive.layer >>> WrapperB.layer
    )
}

The fix is to change val into def

  object PrinterLive {
    def layer: ZLayer[String, Nothing, Printer] = ZLayer.fromFunction(PrinterLive(_))
  }
@desavitsky
Copy link

desavitsky commented Apr 11, 2024

The more idiomatic solution is to use fresh, I think

    val layer: ZLayer[String, Nothing, Printer] = ZLayer.fromFunction(PrinterLive(_)).fresh

If I remember correctly, by default ZLayer instances are shared. And fresh Creates a fresh version of this layer that will not be shared.

@jdegoes
Copy link
Member

jdegoes commented May 8, 2024

For any given type, you can have at most one service that implements that type. So layer sharing is on by default.

One workaround is to create a case class wrapper that has the modified behavior you need for the subgraph of your application that needs it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants