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

fix ambiguity checks in ZLayer#provide macro #8580

Merged

Conversation

myazinn
Copy link
Contributor

@myazinn myazinn commented Dec 20, 2023

Zlayer#provide* macro family sometimes allows having duplicate layers for the same dependency. Here's an example

trait Repository {
  def upsert(data: Any): UIO[Unit]
}

case class MongoRepository() extends Repository {
  override def upsert(data: Any): UIO[Unit] = ZIO.unit
}
object MongoRepository {
  val live: ULayer[MongoRepository] = ZLayer.fromFunction(MongoRepository.apply _)
}

case class GreenplumRepository() extends Repository {
  override def upsert(data: Any): UIO[Unit] = ZIO.unit
}
object GreenplumRepository {
  val live: ULayer[GreenplumRepository] = ZLayer.fromFunction(GreenplumRepository.apply _)
}

case class RepositoryLive(mongo: MongoRepository, gp: GreenplumRepository) extends Repository {
  override def upsert(data: Any): UIO[Unit] = mongo.upsert(data) <&> gp.upsert(data)
}
object RepositoryLive {
  val live: URLayer[MongoRepository with GreenplumRepository, RepositoryLive] = ZLayer.fromFunction(RepositoryLive.apply _)
}

In the above example we've got three different repository implementations, one of which is a combination of two of them.
If we use them like this

ZIO.service[Repository].provide(RepositoryLive.live, MongoRepository.live, GreenplumRepository.live)

The code will compile fine without any warnings. Though it will do an expected thing (it will actually construct and use RepositoryLive), this is quite unexpected since any of three layers could've been used. If we just change their order like this

ZIO.service[Repository].provide(MongoRepository.live, RepositoryLive.live, GreenplumRepository.live)

then it will produce a warning that RepositoryLive.live and GreenplumRepository.live are unused. But overall I'd say that in this case the behaviour is undefined and should be allowed. That's why this MR exists ¯_(ツ)_/¯

val subtypingDuplicates: Map[String, List[String]] =
groupMap {
(target ++ providedLayerNodes.flatMap(_.inputs))
.filterNot(typeEquals(anyType, _))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a stupid hack, but without it the code in this repo (and I assume in many other places) won't compile. It fails with an ambiquity error zio.ZAny is provided by: which looks like some Scala type inference issue, since type ZAny >: Any.
So here we just throw away any type for which Any <:< tpe is true (including Any apparently).

@myazinn myazinn force-pushed the fix-ambiguity-checks-in-provide-macro branch from d568072 to e2936bb Compare December 20, 2023 17:46
Copy link
Contributor

@adamgfraser adamgfraser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! 🙏

@adamgfraser adamgfraser merged commit 5fc57bc into zio:series/2.x Dec 23, 2023
20 checks passed
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

Successfully merging this pull request may close these issues.

None yet

2 participants