Skip to content

Commit

Permalink
Revert "Build fresh layers once with ZLayer macro (#8678)" (#8768)
Browse files Browse the repository at this point in the history
  • Loading branch information
ghostdogpr committed Apr 20, 2024
1 parent f3ad39d commit 52b15e2
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 127 deletions.
19 changes: 0 additions & 19 deletions core-tests/shared/src/test/scala/zio/autowire/AutoWireSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,25 +131,6 @@ object AutoWireSpec extends ZIOBaseSpec {
val provided = ZIO.service[Int].provideLayer(layer)
assertZIO(provided)(equalTo(128))
},
test("building fresh layer only once") {
var timesBuilt = 0

trait Service1
object Service1 {
val live: ZLayer[Any, Throwable, Service1] =
ZLayer.fromZIO(ZIO.attempt(timesBuilt += 1).as(new Service1 {}))
}

trait Service2
object Service2 {
val live: ZLayer[Service1, Nothing, Service2] =
ZLayer.fromZIO(ZIO.unit.as(new Service2 {}))
}

assertZIO(
ZLayer.make[Service1 & Service2](Service1.live.fresh, Service2.live).build *> ZIO.succeed(timesBuilt)
)(equalTo(1))
},
test("correctly decomposes nested, aliased intersection types") {
type StringAlias = String
type HasBooleanDoubleAlias = Boolean with Double
Expand Down
119 changes: 13 additions & 106 deletions core/shared/src/main/scala/zio/internal/macros/Graph.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,114 +2,28 @@ package zio.internal.macros

import zio.internal.macros.LayerTree._

final case class Graph[Key, A](
nodes: List[Node[Key, A]],
keyEquals: (Key, Key) => Boolean,
environment: Key => Node[Key, A]
) {
final case class Graph[Key, A](nodes: List[Node[Key, A]], keyEquals: (Key, Key) => Boolean) {

// Map assigning to each type the times that it must be built
private var neededKeys: Map[Key, Int] = Map.empty
// Dependencies to pass to next iteration of buildComplete
private var dependencies: List[Key] = Nil

def buildNodes(outputs: List[Key], nodes: List[Node[Key, A]]): Either[::[GraphError[Key, A]], LayerTree[A]] = for {
_ <- neededKeys((outputs ++ nodes.flatMap(_.inputs)).distinct)
sideEffects <- forEach(nodes)(buildNode).map(_.combineHorizontally)
rightTree <- build(outputs)
leftTree <- buildComplete(dependencies.distinct)
} yield leftTree >>> (rightTree ++ sideEffects)

private def buildComplete(outputs: List[Key]): Either[::[GraphError[Key, A]], LayerTree[A]] =
if (!outputs.isEmpty)
for {
_ <- Right(restartKeys())
_ <- neededKeys(outputs)
rightTree <- build(outputs)
leftTree <- buildComplete(dependencies.distinct)
} yield leftTree >>> rightTree
else Right(LayerTree.empty)

/**
* Restarts variables for next iteration of buildComplete
*/
private def restartKeys(): Unit = {
neededKeys = Map.empty
dependencies = Nil
}

/**
* Initializes neededKeys
*/
private def neededKeys(
outputs: List[Key],
seen: Set[Node[Key, A]] = Set.empty,
parent: Option[Node[Key, A]] = None
): Either[::[GraphError[Key, A]], Unit] =
forEach(outputs) { output =>
for {
node <- parent match {
case Some(p) =>
getNodeWithOutput[GraphError[Key, A]](
output,
error = GraphError.missingTransitiveDependency(p, output)
)
case None =>
getNodeWithOutput[GraphError[Key, A]](output, error = GraphError.MissingTopLevelDependency(output))
}
_ <- Right(addKey(output))
_ <- parent match {
case Some(p) => assertNonCircularDependency(p, seen, node)
case None => Right(())
}
_ <- neededKeys(node.inputs, seen + node, Some(node))
} yield ()
}.map(_ => ())

private def addKey(key: Key): Unit =
neededKeys.get(key) match {
case Some(n) => neededKeys = neededKeys + (key -> (n + 1))
case None => neededKeys = neededKeys + (key -> 1)
}

/**
* Builds a layer containing only types that appears once. Types appearing
* more than once are replaced with ZLayer.environment[_] and left for the
* next iteration of buildComplete to create.
*/
private def build(outputs: List[Key]): Either[::[GraphError[Key, A]], LayerTree[A]] =
def buildComplete(outputs: List[Key]): Either[::[GraphError[Key, A]], LayerTree[A]] =
forEach(outputs) { output =>
neededKeys.get(output) match {
case None => Right(LayerTree.empty)
case Some(1) =>
getNodeWithOutput[GraphError[Key, A]](output, error = GraphError.MissingTopLevelDependency(output))
.flatMap(node => buildNode(node, Set(node)))
case Some(n) => {
dependencies = output :: dependencies
Right(LayerTree.succeed(environment(output).value))
}
}
getNodeWithOutput[GraphError[Key, A]](output, error = GraphError.MissingTopLevelDependency(output))
.flatMap(node => buildNode(node, Set(node)))
}
.map(_.distinct.combineHorizontally)

def buildNodes(nodes: List[Node[Key, A]]): Either[::[GraphError[Key, A]], LayerTree[A]] =
forEach(nodes)(buildNode).map(_.combineHorizontally)

private def buildNode(node: Node[Key, A]): Either[::[GraphError[Key, A]], LayerTree[A]] =
forEach(node.inputs) { output =>
neededKeys.get(output) match {
case None => Right(LayerTree.empty)
case Some(1) =>
getNodeWithOutput[GraphError[Key, A]](output, error = GraphError.MissingTopLevelDependency(output))
.flatMap(node => buildNode(node, Set(node)))
case Some(n) => {
dependencies = output :: dependencies
Right(LayerTree.empty)
}
}
getNodeWithOutput[GraphError[Key, A]](output, error = GraphError.missingTransitiveDependency(node, output))
.flatMap(node => buildNode(node, Set(node)))
}
.map(_.distinct.combineHorizontally)
.map(_ >>> LayerTree.succeed(node.value))

def map[B](f: A => B): Graph[Key, B] =
Graph(nodes.map(_.map(f)), keyEquals, key => environment(key).map(f))
Graph(nodes.map(_.map(f)), keyEquals)

private def getNodeWithOutput[E](output: Key, error: E): Either[::[E], Node[Key, A]] =
nodes.find(_.outputs.exists(keyEquals(_, output))).toRight(::(error, Nil))
Expand All @@ -120,16 +34,9 @@ final case class Graph[Key, A](
): Either[::[GraphError[Key, A]], LayerTree[A]] =
forEach(node.inputs) { input =>
for {
out <- getNodeWithOutput(input, error = GraphError.missingTransitiveDependency(node, input))
_ <- assertNonCircularDependency(node, seen, out)
result <- neededKeys.get(input) match {
case None => Left(::(GraphError.missingTransitiveDependency(node, input), Nil))
case Some(1) => buildNode(out, seen + out)
case Some(n) => {
dependencies = input :: dependencies
Right(LayerTree.empty)
}
}
out <- getNodeWithOutput(input, error = GraphError.missingTransitiveDependency(node, input))
_ <- assertNonCircularDependency(node, seen, out)
result <- buildNode(out, seen + out)
} yield result
}.map {
_.distinct.combineHorizontally >>> LayerTree.succeed(node.value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,12 @@ final case class LayerBuilder[Type, Expr](
*/
val layerTreeEither: Either[::[GraphError[Type, Expr]], LayerTree[Expr]] = {
val nodes: List[Node[Type, Expr]] = providedLayerNodes ++ remainderNodes ++ sideEffectNodes
val graph = Graph(nodes, typeEquals, typeToNode)
val graph = Graph(nodes, typeEquals)

graph.buildNodes(target, sideEffectNodes)
for {
original <- graph.buildComplete(target)
sideEffects <- graph.buildNodes(sideEffectNodes)
} yield sideEffects ++ original
}

layerTreeEither match {
Expand Down

0 comments on commit 52b15e2

Please sign in to comment.