From b33274511393104d0ab2fc07d28d94b6c699108c Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Mon, 11 Dec 2023 21:51:21 -0500 Subject: [PATCH 01/11] Added Layers trait --- .../shared/src/main/scala/kyo/layers.scala | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 kyo-core/shared/src/main/scala/kyo/layers.scala diff --git a/kyo-core/shared/src/main/scala/kyo/layers.scala b/kyo-core/shared/src/main/scala/kyo/layers.scala new file mode 100644 index 000000000..3d02e8a9a --- /dev/null +++ b/kyo-core/shared/src/main/scala/kyo/layers.scala @@ -0,0 +1,144 @@ +package kyo + +object layers { + + trait Layer[Sin, Sout] { self => + def run[T, S](effect: T > (S with Sout))(implicit fl: Flat[T]): T > (S with Sin) + + final def ++[Sin1, Sout1](other: Layer[Sin1, Sout1]): Layer[Sin with Sin1, Sout with Sout1] = + new Layer[Sin with Sin1, Sout with Sout1] { + override def run[T, S]( + effect: T > (S with Sout with Sout1) + )( + implicit fl: Flat[T] + ): T > (S with Sin with Sin1) = { + val selfRun: T > (S with Sout1 with Sin) = + self.run[T, S with Sout1](effect: T > (S with Sout1 with Sout)) + val otherRun: T > (S with Sin with Sin1) = other.run[T, S with Sin](selfRun) + otherRun + } + } + + final def >>>[Sin1, Sout1](other: Layer[Sin1, Sout1])(implicit + ap: ApplyLayer[Sout, Sin1] + ): Layer[Sin with ap.Sin, Sout1 with ap.Sout] = { + ap.applyLayer[Sin, Sout1](self, other) + } + } + + sealed trait ApplyLayer[S1, S2] { + type Sin + type Sout + + def applyLayer[Sin1, Sout2]( + layer1: Layer[Sin1, S1], + layer2: Layer[S2, Sout2] + ): Layer[Sin1 with Sin, Sout2 with Sout] + } + + trait LowPriorityApplyLayers1 { + implicit def partialApplication[Sshared, Sextra1, Sextra2] + : ApplyLayer[Sshared with Sextra1, Sshared with Sextra2] { + type Sin = Sextra2; type Sout = Sextra1 + } = { + new ApplyLayer[Sshared with Sextra1, Sshared with Sextra2] { + type Sin = Sextra2 + type Sout = Sextra1 + + override def applyLayer[Sin1, Sout2]( + layer1: Layer[Sin1, Sshared with Sextra1], + layer2: Layer[Sshared with Sextra2, Sout2] + ): Layer[Sin1 with Sextra2, Sout2 with Sextra1] = { + new Layer[Sin1 with Sextra2, Sout2 with Sextra1] { + override def run[T, S]( + effect: T > (S with Sout2 with Sextra1) + )( + implicit fl: Flat[T] + ): T > (S with Sin1 with Sextra2) = { + val run2: T > (S with Sshared with Sextra2 with Sextra1) = + layer2.run[T, S with Sextra1](effect) + val run1: T > (S with Sin1 with Sextra2) = layer1.run[T, S with Sextra2](run2) + run1 + } + } + } + } + } + } + + trait LowPriorityApplyLayers extends LowPriorityApplyLayers1 { + implicit def partialApplicationLeft[Sshared, Sextra] + : ApplyLayer[Sshared, Sshared with Sextra] { type Sin = Sextra; type Sout = Any } = { + new ApplyLayer[Sshared, Sshared with Sextra] { + type Sin = Sextra + type Sout = Any + + def applyLayer[Sin1, Sout2]( + layer1: Layer[Sin1, Sshared], + layer2: Layer[Sshared with Sextra, Sout2] + ): Layer[Sin1 with Sextra, Sout2] = { + new Layer[Sin1 with Sextra, Sout2] { + override def run[T, S](effect: T > (S with Sout2))( + implicit fl: Flat[T] + ): T > (S with Sin1 with Sextra) = { + val run2: T > (S with Sshared with Sextra) = layer2.run[T, S](effect) + val run1: T > (S with Sextra with Sin1) = layer1.run[T, S with Sextra](run2) + run1 + } + } + } + } + } + + implicit def partialApplicationRight[Sshared, Sextra] + : ApplyLayer[Sshared with Sextra, Sshared] { type Sin = Any; type Sout = Sextra } = { + new ApplyLayer[Sshared with Sextra, Sshared] { + type Sin = Any + type Sout = Sextra + + override def applyLayer[Sin1, Sout2]( + layer1: Layer[Sin1, Sshared with Sextra], + layer2: Layer[Sshared, Sout2] + ): Layer[Sin1 with Any, Sout2 with Sextra] = { + new Layer[Sin1 with Any, Sout2 with Sextra] { + override def run[T, S]( + effect: T > (S with Sout2 with Sextra) + )( + implicit fl: Flat[T] + ): T > (S with Sin1) = { + val run2: T > (S with Sshared with Sextra) = layer2.run[T, S with Sextra](effect) + val run1: T > (S with Sin1) = layer1.run[T, S](run2) + run1 + } + } + } + } + } + } + + object ApplyLayer extends LowPriorityApplyLayers { + type Aux[S1, S2, Sin0, Sout0] = ApplyLayer[S1, S2] { type Sin = Sin0; type Sout = Sout0 } + + implicit def simpleApplication[Sshare]: ApplyLayer.Aux[Sshare, Sshare, Any, Any] = + new ApplyLayer[Sshare, Sshare] { + type Sin = Any + type Sout = Any + + def applyLayer[Sin1, Sout2]( + layer1: Layer[Sin1, Sshare], + layer2: Layer[Sshare, Sout2] + ): Layer[Sin1, Sout2] = { + new Layer[Sin1, Sout2] { + override def run[T, S](effect: T > (S with Sout2))(implicit + fl: Flat[T] + ): T > (S with Sin1) = { + val run2: T > (S with Sshare) = layer2.run[T, S](effect) + val run1: T > (S with Sin1) = layer1.run[T, S](run2) + run1 + } + } + } + } + } + +} From b091de7094320339095a3dc4d92e204421084252 Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Mon, 11 Dec 2023 21:52:32 -0500 Subject: [PATCH 02/11] Added Envs layer factory method --- kyo-core/shared/src/main/scala/kyo/envs.scala | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/kyo-core/shared/src/main/scala/kyo/envs.scala b/kyo-core/shared/src/main/scala/kyo/envs.scala index 560643e6b..44324984b 100644 --- a/kyo-core/shared/src/main/scala/kyo/envs.scala +++ b/kyo-core/shared/src/main/scala/kyo/envs.scala @@ -5,6 +5,7 @@ import izumi.reflect._ import scala.reflect.ClassTag import kyo.core._ +import kyo.layers.Layer object envs { @@ -15,7 +16,7 @@ object envs { } final class Envs[E] private[envs] (implicit private val tag: Tag[_]) - extends Effect[Env[E]#Value, Envs[E]] { + extends Effect[Env[E]#Value, Envs[E]] { self => val get: E > Envs[E] = suspend(Input.asInstanceOf[Env[E]#Value[E]]) @@ -47,6 +48,14 @@ object envs { } override def toString = s"Envs[${tag.tag.longNameWithPrefix}]" + + def layer[Sd](construct: E > Sd): Layer[Sd, Envs[E]] = + new Layer[Sd, Envs[E]] { + override def run[T, S](effect: T > (S with Envs[E]))(implicit + fl: Flat[T] + ): T > (S with Sd) = + construct.map(e => self.run[T, S](e)(effect: T > (Envs[E] with S))) + } } object Envs { From b5bfcd037740343716bf5e71b72cd599d07ea07c Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Mon, 11 Dec 2023 21:52:49 -0500 Subject: [PATCH 03/11] Added Aborts layer factory method --- kyo-core/shared/src/main/scala/kyo/aborts.scala | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/kyo-core/shared/src/main/scala/kyo/aborts.scala b/kyo-core/shared/src/main/scala/kyo/aborts.scala index 7c31471c7..247f26797 100644 --- a/kyo-core/shared/src/main/scala/kyo/aborts.scala +++ b/kyo-core/shared/src/main/scala/kyo/aborts.scala @@ -10,6 +10,7 @@ import scala.util.Success import kyo._ import core._ import tries._ +import layers._ import scala.util.Try object aborts { @@ -28,7 +29,7 @@ object aborts { } final class Aborts[E] private[aborts] (private val tag: Tag[E]) - extends Effect[Abort[E]#Value, Aborts[E]] { + extends Effect[Abort[E]#Value, Aborts[E]] { self => private implicit def _tag: Tag[E] = tag @@ -91,5 +92,19 @@ object aborts { } override def toString = s"Aborts[${tag.tag.longNameWithPrefix}]" + + def layer[Se](handle: E => Nothing > Se): Layer[Se, Aborts[E]] = + new Layer[Se, Aborts[E]] { + override def run[T, S]( + effect: T > (S with Aborts[E]) + )( + implicit fl: Flat[T] + ): T > (S with Se) = + self.run[T, S](effect).map { + case Left(err) => handle(err) + case Right(t) => t + } + } } + } From fa7f2f1af2ace455f41218719ba3f1f63c5ef851 Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Mon, 11 Dec 2023 21:53:13 -0500 Subject: [PATCH 04/11] Added Tries layer factory method --- kyo-core/shared/src/main/scala/kyo/tries.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kyo-core/shared/src/main/scala/kyo/tries.scala b/kyo-core/shared/src/main/scala/kyo/tries.scala index 1932b1c08..1762ff2ef 100644 --- a/kyo-core/shared/src/main/scala/kyo/tries.scala +++ b/kyo-core/shared/src/main/scala/kyo/tries.scala @@ -1,6 +1,7 @@ package kyo import kyo.aborts._ +import kyo.layers._ import scala.util._ @@ -43,5 +44,14 @@ object tries { case Failure(ex) => fail(ex) } + + def layer[Se](handle: Throwable => Nothing > Se): Layer[Se, Tries] = + new Layer[Se, Tries] { + override def run[T, S](effect: T > (S with Tries))(implicit fl: Flat[T]): T > (S with Se) = + Tries.run[T, S](effect).map { + case Failure(exception) => handle(exception) + case Success(t) => t + } + } } } From 534b65ffc893359431bb238f3ecbbd5743482fb8 Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Mon, 11 Dec 2023 21:53:30 -0500 Subject: [PATCH 05/11] Added Options layer factory method --- kyo-core/shared/src/main/scala/kyo/options.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kyo-core/shared/src/main/scala/kyo/options.scala b/kyo-core/shared/src/main/scala/kyo/options.scala index 7fb40748c..29b7d0463 100644 --- a/kyo-core/shared/src/main/scala/kyo/options.scala +++ b/kyo-core/shared/src/main/scala/kyo/options.scala @@ -1,6 +1,7 @@ package kyo import kyo.aborts._ +import kyo.layers._ object options { @@ -50,5 +51,16 @@ object options { case v => get(v) } } + + def layer[Se](onEmpty: => Nothing > Se): Layer[Se, Options] = + new Layer[Se, Options] { + override def run[T, S](effect: T > (S with Options))(implicit + fl: Flat[T] + ): T > (S with Se) = + Options.run[T, S](effect).map { + case None => onEmpty + case Some(t) => t + } + } } } From 5e4ce170b7e9f2f1da2223445e8f45bf2335bcf3 Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Mon, 11 Dec 2023 21:54:03 -0500 Subject: [PATCH 06/11] Added layers test (testing Envs, Aborts, Options and Tries layers --- .../src/test/scala/kyoTest/layersTest.scala | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 kyo-core/shared/src/test/scala/kyoTest/layersTest.scala diff --git a/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala b/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala new file mode 100644 index 000000000..b5bd9a119 --- /dev/null +++ b/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala @@ -0,0 +1,118 @@ +package kyoTest + +import kyo._ +import kyo.envs._ +import kyo.aborts._ +import kyo.tries._ +import kyo.options._ +import scala.util.Failure + +class layersTest extends KyoTest { + + final case class Dep1(int: Int) + final case class Dep2(str: String) + final case class Dep3(bool: Boolean) + + final case class Dep(dep1: Int, dep2: String, dep3: Boolean) + + val depLayer = Envs[Dep].layer(Dep(1, "hello", true)) + val dep1Layer = Envs[Dep1].layer(Envs[Dep].get.map(v => Dep1(v.dep1))) + val dep2Layer = Envs[Dep2].layer(Envs[Dep].get.map(v => Dep2(v.dep2))) + val dep3Layer = Envs[Dep3].layer(Envs[Dep].get.map(v => Dep3(v.dep3))) + + "Envs layers should be composable and provide multiple dependencies" in { + val layer = depLayer >>> (dep1Layer ++ dep2Layer ++ dep3Layer) + + val effect = for { + dep1 <- Envs[Dep1].get + dep2 <- Envs[Dep2].get + dep3 <- Envs[Dep3].get + } yield s"${dep1.int}-${dep2.str}-${dep3.bool}" + + val handledEffect = layer.run(effect) + + assert(handledEffect == "1-hello-true") + } + + final case class TestError1(msg: String) + final case class TestError2(msg: String) + val stringToTE1Layer = Aborts[String].layer(str => Aborts[TestError1].fail(TestError1(str))) + val dep1ToTE1Layer = Aborts[Dep2].layer(dep => Aborts[TestError1].fail(TestError1(dep.str))) + val throwableToTE1Layer = + Aborts[Throwable].layer(err => Aborts[TestError1].fail(TestError1(err.getMessage()))) + val testError1ToTE2Layer = + Aborts[TestError1].layer(err => Aborts[TestError2].fail(TestError2(err.msg))) + + "Aborts layers should be composable and handle multiple error types" in { + val layer = testError1ToTE2Layer >>> (stringToTE1Layer ++ dep1ToTE1Layer ++ throwableToTE1Layer) + + val effect1 = for { + _ <- Aborts[String].fail("string failure") + _ <- Aborts[Dep2].fail(Dep2("dep2 failure")) + _ <- Aborts[Throwable].fail(new Exception("throwable failure")) + } yield () + + val effect2 = for { + _ <- Aborts[Throwable].fail(new Exception("throwable failure")) + _ <- Aborts[String].fail("string failure") + _ <- Aborts[Dep2].fail(Dep2("dep2 failure")) + } yield () + + val effect3 = for { + _ <- Aborts[Dep2].fail(Dep2("dep2 failure")) + _ <- Aborts[Throwable].fail(new Exception("throwable failure")) + _ <- Aborts[String].fail("string failure") + } yield () + + assert(Aborts[TestError2].run(layer.run(effect1)) == Left(TestError2("string failure"))) + assert(Aborts[TestError2].run(layer.run(effect2)) == Left(TestError2("throwable failure"))) + assert(Aborts[TestError2].run(layer.run(effect3)) == Left(TestError2("dep2 failure"))) + } + + val triesToAbortsLayer = Tries.layer(err => Aborts[Throwable].fail(err)) + val triesToOptionsLayer = Tries.layer(_ => Options.get(None)) + + "Tries layer should handle tries as other failures" in { + val effect = for { + _ <- Tries.fail("fail") + } yield () + + val effectHandledToAborts = triesToAbortsLayer.run(effect) + val effectHandledToOptions = triesToOptionsLayer.run(effect) + + assert { + Aborts[Throwable].run(effectHandledToAborts) match { + case Left(err: Throwable) => err.getMessage == "fail" + case _ => false + } + } + assert(Options.run(effectHandledToOptions) == None) + } + + val optionsToAbortsLayer = Options.layer(Aborts[String].fail("missing value")) + val optionsToTriesLayer = Options.layer(Tries.fail("missing value")) + + "Options layer should handle None as other failures" in { + val effect = for { + _ <- Options.get(None) + } yield () + + val effectHandledToAborts = optionsToAbortsLayer.run(effect) + val effectHandledToTries = optionsToTriesLayer.run(effect) + + assert { + Aborts[String].run(effectHandledToAborts) match { + case Left("missing value") => true + case _ => false + } + } + + assert { + Tries.run(effectHandledToTries) match { + case Failure(err: Throwable) => err.getMessage == "missing value" + case _ => false + } + } + } + +} From 36334b18da927fd6c9742044d6598e2578e581c6 Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Tue, 12 Dec 2023 11:27:20 -0500 Subject: [PATCH 07/11] Updated ++/>>> to add/andThen; fixed Flat implicits --- .../shared/src/main/scala/kyo/aborts.scala | 4 +-- kyo-core/shared/src/main/scala/kyo/envs.scala | 2 +- .../shared/src/main/scala/kyo/layers.scala | 25 +++++++++++-------- .../shared/src/main/scala/kyo/options.scala | 2 +- .../shared/src/main/scala/kyo/tries.scala | 4 ++- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/kyo-core/shared/src/main/scala/kyo/aborts.scala b/kyo-core/shared/src/main/scala/kyo/aborts.scala index 247f26797..89af0a2dd 100644 --- a/kyo-core/shared/src/main/scala/kyo/aborts.scala +++ b/kyo-core/shared/src/main/scala/kyo/aborts.scala @@ -98,9 +98,9 @@ object aborts { override def run[T, S]( effect: T > (S with Aborts[E]) )( - implicit fl: Flat[T] + implicit flat: Flat[T > (S with Aborts[E])] ): T > (S with Se) = - self.run[T, S](effect).map { + self.run[T, S](effect)(flat).map { case Left(err) => handle(err) case Right(t) => t } diff --git a/kyo-core/shared/src/main/scala/kyo/envs.scala b/kyo-core/shared/src/main/scala/kyo/envs.scala index 44324984b..adcff924b 100644 --- a/kyo-core/shared/src/main/scala/kyo/envs.scala +++ b/kyo-core/shared/src/main/scala/kyo/envs.scala @@ -52,7 +52,7 @@ object envs { def layer[Sd](construct: E > Sd): Layer[Sd, Envs[E]] = new Layer[Sd, Envs[E]] { override def run[T, S](effect: T > (S with Envs[E]))(implicit - fl: Flat[T] + fl: Flat[T > (S with Envs[E])] ): T > (S with Sd) = construct.map(e => self.run[T, S](e)(effect: T > (Envs[E] with S))) } diff --git a/kyo-core/shared/src/main/scala/kyo/layers.scala b/kyo-core/shared/src/main/scala/kyo/layers.scala index 3d02e8a9a..351a5e4d7 100644 --- a/kyo-core/shared/src/main/scala/kyo/layers.scala +++ b/kyo-core/shared/src/main/scala/kyo/layers.scala @@ -3,18 +3,19 @@ package kyo object layers { trait Layer[Sin, Sout] { self => - def run[T, S](effect: T > (S with Sout))(implicit fl: Flat[T]): T > (S with Sin) + def run[T, S](effect: T > (S with Sout))(implicit fl: Flat[T > (S with Sout)]): T > (S with Sin) final def ++[Sin1, Sout1](other: Layer[Sin1, Sout1]): Layer[Sin with Sin1, Sout with Sout1] = new Layer[Sin with Sin1, Sout with Sout1] { override def run[T, S]( effect: T > (S with Sout with Sout1) )( - implicit fl: Flat[T] + implicit fl: Flat[T > (S with Sout with Sout1)] ): T > (S with Sin with Sin1) = { val selfRun: T > (S with Sout1 with Sin) = self.run[T, S with Sout1](effect: T > (S with Sout1 with Sout)) - val otherRun: T > (S with Sin with Sin1) = other.run[T, S with Sin](selfRun) + val otherRun: T > (S with Sin with Sin1) = + other.run[T, S with Sin](selfRun)(Flat.unsafe.unchecked) otherRun } } @@ -53,11 +54,12 @@ object layers { override def run[T, S]( effect: T > (S with Sout2 with Sextra1) )( - implicit fl: Flat[T] + implicit fl: Flat[T > (S with Sout2 with Sextra1)] ): T > (S with Sin1 with Sextra2) = { val run2: T > (S with Sshared with Sextra2 with Sextra1) = layer2.run[T, S with Sextra1](effect) - val run1: T > (S with Sin1 with Sextra2) = layer1.run[T, S with Sextra2](run2) + val run1: T > (S with Sin1 with Sextra2) = + layer1.run[T, S with Sextra2](run2)(Flat.unsafe.unchecked) run1 } } @@ -79,10 +81,11 @@ object layers { ): Layer[Sin1 with Sextra, Sout2] = { new Layer[Sin1 with Sextra, Sout2] { override def run[T, S](effect: T > (S with Sout2))( - implicit fl: Flat[T] + implicit fl: Flat[T > (S with Sout2)] ): T > (S with Sin1 with Sextra) = { val run2: T > (S with Sshared with Sextra) = layer2.run[T, S](effect) - val run1: T > (S with Sextra with Sin1) = layer1.run[T, S with Sextra](run2) + val run1: T > (S with Sextra with Sin1) = + layer1.run[T, S with Sextra](run2)(Flat.unsafe.unchecked) run1 } } @@ -104,10 +107,10 @@ object layers { override def run[T, S]( effect: T > (S with Sout2 with Sextra) )( - implicit fl: Flat[T] + implicit fl: Flat[T > (S with Sout2 with Sextra)] ): T > (S with Sin1) = { val run2: T > (S with Sshared with Sextra) = layer2.run[T, S with Sextra](effect) - val run1: T > (S with Sin1) = layer1.run[T, S](run2) + val run1: T > (S with Sin1) = layer1.run[T, S](run2)(Flat.unsafe.unchecked) run1 } } @@ -130,10 +133,10 @@ object layers { ): Layer[Sin1, Sout2] = { new Layer[Sin1, Sout2] { override def run[T, S](effect: T > (S with Sout2))(implicit - fl: Flat[T] + fl: Flat[T > (S with Sout2)] ): T > (S with Sin1) = { val run2: T > (S with Sshare) = layer2.run[T, S](effect) - val run1: T > (S with Sin1) = layer1.run[T, S](run2) + val run1: T > (S with Sin1) = layer1.run[T, S](run2)(Flat.unsafe.unchecked) run1 } } diff --git a/kyo-core/shared/src/main/scala/kyo/options.scala b/kyo-core/shared/src/main/scala/kyo/options.scala index 29b7d0463..d2c0af039 100644 --- a/kyo-core/shared/src/main/scala/kyo/options.scala +++ b/kyo-core/shared/src/main/scala/kyo/options.scala @@ -55,7 +55,7 @@ object options { def layer[Se](onEmpty: => Nothing > Se): Layer[Se, Options] = new Layer[Se, Options] { override def run[T, S](effect: T > (S with Options))(implicit - fl: Flat[T] + fl: Flat[T > (S with Options)] ): T > (S with Se) = Options.run[T, S](effect).map { case None => onEmpty diff --git a/kyo-core/shared/src/main/scala/kyo/tries.scala b/kyo-core/shared/src/main/scala/kyo/tries.scala index 1762ff2ef..ef9a881d0 100644 --- a/kyo-core/shared/src/main/scala/kyo/tries.scala +++ b/kyo-core/shared/src/main/scala/kyo/tries.scala @@ -47,7 +47,9 @@ object tries { def layer[Se](handle: Throwable => Nothing > Se): Layer[Se, Tries] = new Layer[Se, Tries] { - override def run[T, S](effect: T > (S with Tries))(implicit fl: Flat[T]): T > (S with Se) = + override def run[T, S](effect: T > (S with Tries))(implicit + fl: Flat[T > (S with Tries)] + ): T > (S with Se) = Tries.run[T, S](effect).map { case Failure(exception) => handle(exception) case Success(t) => t From 68b6fd2c730068f4c77d8300f634342ce5e5339d Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Tue, 12 Dec 2023 13:35:26 -0500 Subject: [PATCH 08/11] Fixed Layer type parameters and order; changed andThen to chain and simplified ChainLayer type class --- .../shared/src/main/scala/kyo/aborts.scala | 4 +- kyo-core/shared/src/main/scala/kyo/envs.scala | 4 +- .../shared/src/main/scala/kyo/layers.scala | 179 ++++++------------ .../shared/src/main/scala/kyo/options.scala | 4 +- .../shared/src/main/scala/kyo/tries.scala | 4 +- .../src/test/scala/kyoTest/layersTest.scala | 6 +- 6 files changed, 73 insertions(+), 128 deletions(-) diff --git a/kyo-core/shared/src/main/scala/kyo/aborts.scala b/kyo-core/shared/src/main/scala/kyo/aborts.scala index 89af0a2dd..f96b68a49 100644 --- a/kyo-core/shared/src/main/scala/kyo/aborts.scala +++ b/kyo-core/shared/src/main/scala/kyo/aborts.scala @@ -93,8 +93,8 @@ object aborts { override def toString = s"Aborts[${tag.tag.longNameWithPrefix}]" - def layer[Se](handle: E => Nothing > Se): Layer[Se, Aborts[E]] = - new Layer[Se, Aborts[E]] { + def layer[Se](handle: E => Nothing > Se): Layer[Aborts[E], Se] = + new Layer[Aborts[E], Se] { override def run[T, S]( effect: T > (S with Aborts[E]) )( diff --git a/kyo-core/shared/src/main/scala/kyo/envs.scala b/kyo-core/shared/src/main/scala/kyo/envs.scala index adcff924b..7bd051556 100644 --- a/kyo-core/shared/src/main/scala/kyo/envs.scala +++ b/kyo-core/shared/src/main/scala/kyo/envs.scala @@ -49,8 +49,8 @@ object envs { override def toString = s"Envs[${tag.tag.longNameWithPrefix}]" - def layer[Sd](construct: E > Sd): Layer[Sd, Envs[E]] = - new Layer[Sd, Envs[E]] { + def layer[Sd](construct: E > Sd): Layer[Envs[E], Sd] = + new Layer[Envs[E], Sd] { override def run[T, S](effect: T > (S with Envs[E]))(implicit fl: Flat[T > (S with Envs[E])] ): T > (S with Sd) = diff --git a/kyo-core/shared/src/main/scala/kyo/layers.scala b/kyo-core/shared/src/main/scala/kyo/layers.scala index 351a5e4d7..864f7864c 100644 --- a/kyo-core/shared/src/main/scala/kyo/layers.scala +++ b/kyo-core/shared/src/main/scala/kyo/layers.scala @@ -2,145 +2,88 @@ package kyo object layers { - trait Layer[Sin, Sout] { self => - def run[T, S](effect: T > (S with Sout))(implicit fl: Flat[T > (S with Sout)]): T > (S with Sin) + trait Layer[In, Out] { self => + def run[T, S](effect: T > (S with In))(implicit fl: Flat[T > (S with In)]): T > (S with Out) - final def ++[Sin1, Sout1](other: Layer[Sin1, Sout1]): Layer[Sin with Sin1, Sout with Sout1] = - new Layer[Sin with Sin1, Sout with Sout1] { + final def add[Out1, In1](other: Layer[In1, Out1]): Layer[In with In1, Out with Out1] = + new Layer[In with In1, Out with Out1] { override def run[T, S]( - effect: T > (S with Sout with Sout1) + effect: T > (S with In with In1) )( - implicit fl: Flat[T > (S with Sout with Sout1)] - ): T > (S with Sin with Sin1) = { - val selfRun: T > (S with Sout1 with Sin) = - self.run[T, S with Sout1](effect: T > (S with Sout1 with Sout)) - val otherRun: T > (S with Sin with Sin1) = - other.run[T, S with Sin](selfRun)(Flat.unsafe.unchecked) + implicit fl: Flat[T > (S with In with In1)] + ): T > (S with Out with Out1) = { + val selfRun: T > (S with In1 with Out) = + self.run[T, S with In1](effect: T > (S with In1 with In)) + val otherRun: T > (S with Out with Out1) = + other.run[T, S with Out](selfRun)(Flat.unsafe.unchecked) otherRun } } - final def >>>[Sin1, Sout1](other: Layer[Sin1, Sout1])(implicit - ap: ApplyLayer[Sout, Sin1] - ): Layer[Sin with ap.Sin, Sout1 with ap.Sout] = { - ap.applyLayer[Sin, Sout1](self, other) + final def chain[In2, Out2](other: Layer[In2, Out2])( + implicit ap: ChainLayer[Out, In2] + ): Layer[In, Out2 with ap.RemainingOut1] = { + ap.applyLayer[In, Out2](self, other) } } - sealed trait ApplyLayer[S1, S2] { - type Sin - type Sout + /** Use layer1 to handle unhandled dependencies (Out) of layer2 + */ + sealed trait ChainLayer[Out1, In2] { + type RemainingOut1 - def applyLayer[Sin1, Sout2]( - layer1: Layer[Sin1, S1], - layer2: Layer[S2, Sout2] - ): Layer[Sin1 with Sin, Sout2 with Sout] + def applyLayer[In1, Out2]( + layer1: Layer[In1, Out1], + layer2: Layer[In2, Out2] + ): Layer[In1, RemainingOut1 with Out2] } - trait LowPriorityApplyLayers1 { - implicit def partialApplication[Sshared, Sextra1, Sextra2] - : ApplyLayer[Sshared with Sextra1, Sshared with Sextra2] { - type Sin = Sextra2; type Sout = Sextra1 - } = { - new ApplyLayer[Sshared with Sextra1, Sshared with Sextra2] { - type Sin = Sextra2 - type Sout = Sextra1 - - override def applyLayer[Sin1, Sout2]( - layer1: Layer[Sin1, Sshared with Sextra1], - layer2: Layer[Sshared with Sextra2, Sout2] - ): Layer[Sin1 with Sextra2, Sout2 with Sextra1] = { - new Layer[Sin1 with Sextra2, Sout2 with Sextra1] { - override def run[T, S]( - effect: T > (S with Sout2 with Sextra1) - )( - implicit fl: Flat[T > (S with Sout2 with Sextra1)] - ): T > (S with Sin1 with Sextra2) = { - val run2: T > (S with Sshared with Sextra2 with Sextra1) = - layer2.run[T, S with Sextra1](effect) - val run1: T > (S with Sin1 with Sextra2) = - layer1.run[T, S with Sextra2](run2)(Flat.unsafe.unchecked) - run1 + trait ChainLayers2 { + + implicit def application[Out1, Shared, In2] + : ChainLayer.Aux[Out1 with Shared, In2 with Shared, Out1] = + new ChainLayer[Out1 with Shared, In2 with Shared] { + type RemainingOut1 = Out1 + override def applyLayer[In1, Out2]( + layer1: Layer[In1, Out1 with Shared], + layer2: Layer[In2 with Shared, Out2] + ): Layer[In1, Out1 with Out2] = + new Layer[In1, Out1 with Out2] { + override def run[T, S](effect: T > (S with In1))(implicit + fl: Flat[T > (S with In1)] + ): T > (S with Out2 with Out1) = { + val handled1: T > (S with Out1 with Shared) = layer1.run[T, S](effect) + val handled2: T > (S with Out2 with Out1) = + layer2.run[T, S with Out1](handled1)(Flat.unsafe.unchecked) + handled2 } } - } - } - } - } - - trait LowPriorityApplyLayers extends LowPriorityApplyLayers1 { - implicit def partialApplicationLeft[Sshared, Sextra] - : ApplyLayer[Sshared, Sshared with Sextra] { type Sin = Sextra; type Sout = Any } = { - new ApplyLayer[Sshared, Sshared with Sextra] { - type Sin = Sextra - type Sout = Any - def applyLayer[Sin1, Sout2]( - layer1: Layer[Sin1, Sshared], - layer2: Layer[Sshared with Sextra, Sout2] - ): Layer[Sin1 with Sextra, Sout2] = { - new Layer[Sin1 with Sextra, Sout2] { - override def run[T, S](effect: T > (S with Sout2))( - implicit fl: Flat[T > (S with Sout2)] - ): T > (S with Sin1 with Sextra) = { - val run2: T > (S with Sshared with Sextra) = layer2.run[T, S](effect) - val run1: T > (S with Sextra with Sin1) = - layer1.run[T, S with Sextra](run2)(Flat.unsafe.unchecked) - run1 - } - } - } } - } - - implicit def partialApplicationRight[Sshared, Sextra] - : ApplyLayer[Sshared with Sextra, Sshared] { type Sin = Any; type Sout = Sextra } = { - new ApplyLayer[Sshared with Sextra, Sshared] { - type Sin = Any - type Sout = Sextra - - override def applyLayer[Sin1, Sout2]( - layer1: Layer[Sin1, Sshared with Sextra], - layer2: Layer[Sshared, Sout2] - ): Layer[Sin1 with Any, Sout2 with Sextra] = { - new Layer[Sin1 with Any, Sout2 with Sextra] { - override def run[T, S]( - effect: T > (S with Sout2 with Sextra) - )( - implicit fl: Flat[T > (S with Sout2 with Sextra)] - ): T > (S with Sin1) = { - val run2: T > (S with Sshared with Sextra) = layer2.run[T, S with Sextra](effect) - val run1: T > (S with Sin1) = layer1.run[T, S](run2)(Flat.unsafe.unchecked) - run1 - } - } - } - } - } } - object ApplyLayer extends LowPriorityApplyLayers { - type Aux[S1, S2, Sin0, Sout0] = ApplyLayer[S1, S2] { type Sin = Sin0; type Sout = Sout0 } - - implicit def simpleApplication[Sshare]: ApplyLayer.Aux[Sshare, Sshare, Any, Any] = - new ApplyLayer[Sshare, Sshare] { - type Sin = Any - type Sout = Any - - def applyLayer[Sin1, Sout2]( - layer1: Layer[Sin1, Sshare], - layer2: Layer[Sshare, Sout2] - ): Layer[Sin1, Sout2] = { - new Layer[Sin1, Sout2] { - override def run[T, S](effect: T > (S with Sout2))(implicit - fl: Flat[T > (S with Sout2)] - ): T > (S with Sin1) = { - val run2: T > (S with Sshare) = layer2.run[T, S](effect) - val run1: T > (S with Sin1) = layer1.run[T, S](run2)(Flat.unsafe.unchecked) - run1 + object ChainLayer extends ChainLayers2 { + type Aux[Out1, In2, R] = ChainLayer[Out1, In2] { type RemainingOut1 = R } + + implicit def simpleChain[Out]: ChainLayer.Aux[Out, Out, Any] = + new ChainLayer[Out, Out] { + type RemainingOut1 = Any + + override def applyLayer[In1, Out2]( + layer1: Layer[In1, Out], + layer2: Layer[Out, Out2] + ): Layer[In1, Any with Out2] = + new Layer[In1, Any with Out2] { + override def run[T, S](effect: T > (S with In1))(implicit + fl: Flat[T > (S with In1)] + ): T > (S with Out2) = { + val handled1: T > (S with Out) = layer1.run[T, S](effect) + val handled2: T > (S with Out2) = + layer2.run[T, S](handled1)(Flat.unsafe.unchecked) + handled2 } } - } + } } diff --git a/kyo-core/shared/src/main/scala/kyo/options.scala b/kyo-core/shared/src/main/scala/kyo/options.scala index d2c0af039..d0099bc4e 100644 --- a/kyo-core/shared/src/main/scala/kyo/options.scala +++ b/kyo-core/shared/src/main/scala/kyo/options.scala @@ -52,8 +52,8 @@ object options { } } - def layer[Se](onEmpty: => Nothing > Se): Layer[Se, Options] = - new Layer[Se, Options] { + def layer[Se](onEmpty: => Nothing > Se): Layer[Options, Se] = + new Layer[Options, Se] { override def run[T, S](effect: T > (S with Options))(implicit fl: Flat[T > (S with Options)] ): T > (S with Se) = diff --git a/kyo-core/shared/src/main/scala/kyo/tries.scala b/kyo-core/shared/src/main/scala/kyo/tries.scala index ef9a881d0..10c6477c0 100644 --- a/kyo-core/shared/src/main/scala/kyo/tries.scala +++ b/kyo-core/shared/src/main/scala/kyo/tries.scala @@ -45,8 +45,8 @@ object tries { fail(ex) } - def layer[Se](handle: Throwable => Nothing > Se): Layer[Se, Tries] = - new Layer[Se, Tries] { + def layer[Se](handle: Throwable => Nothing > Se): Layer[Tries, Se] = + new Layer[Tries, Se] { override def run[T, S](effect: T > (S with Tries))(implicit fl: Flat[T > (S with Tries)] ): T > (S with Se) = diff --git a/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala b/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala index b5bd9a119..59ba8bb09 100644 --- a/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala +++ b/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala @@ -5,6 +5,7 @@ import kyo.envs._ import kyo.aborts._ import kyo.tries._ import kyo.options._ +import kyo.layers._ import scala.util.Failure class layersTest extends KyoTest { @@ -21,7 +22,7 @@ class layersTest extends KyoTest { val dep3Layer = Envs[Dep3].layer(Envs[Dep].get.map(v => Dep3(v.dep3))) "Envs layers should be composable and provide multiple dependencies" in { - val layer = depLayer >>> (dep1Layer ++ dep2Layer ++ dep3Layer) + val layer = (dep1Layer add dep2Layer add dep3Layer) chain depLayer val effect = for { dep1 <- Envs[Dep1].get @@ -44,7 +45,8 @@ class layersTest extends KyoTest { Aborts[TestError1].layer(err => Aborts[TestError2].fail(TestError2(err.msg))) "Aborts layers should be composable and handle multiple error types" in { - val layer = testError1ToTE2Layer >>> (stringToTE1Layer ++ dep1ToTE1Layer ++ throwableToTE1Layer) + val layer = + (stringToTE1Layer add dep1ToTE1Layer add throwableToTE1Layer) chain testError1ToTE2Layer val effect1 = for { _ <- Aborts[String].fail("string failure") From a8e09e4050f92d18f8dea9c2015165dd98bc1b55 Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Tue, 12 Dec 2023 13:50:22 -0500 Subject: [PATCH 09/11] Added two more ChainLayer instances to cover edge cases --- .../shared/src/main/scala/kyo/layers.scala | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/kyo-core/shared/src/main/scala/kyo/layers.scala b/kyo-core/shared/src/main/scala/kyo/layers.scala index 864f7864c..5aa7ee054 100644 --- a/kyo-core/shared/src/main/scala/kyo/layers.scala +++ b/kyo-core/shared/src/main/scala/kyo/layers.scala @@ -27,8 +27,6 @@ object layers { } } - /** Use layer1 to handle unhandled dependencies (Out) of layer2 - */ sealed trait ChainLayer[Out1, In2] { type RemainingOut1 @@ -39,7 +37,6 @@ object layers { } trait ChainLayers2 { - implicit def application[Out1, Shared, In2] : ChainLayer.Aux[Out1 with Shared, In2 with Shared, Out1] = new ChainLayer[Out1 with Shared, In2 with Shared] { @@ -62,7 +59,51 @@ object layers { } } - object ChainLayer extends ChainLayers2 { + trait ChainLayers1 { + implicit def applyAll1[Shared, In2]: ChainLayer.Aux[Shared, In2 with Shared, Any] = + new ChainLayer[Shared, In2 with Shared] { + type RemainingOut1 = Any + + override def applyLayer[In1, Out2]( + layer1: Layer[In1, Shared], + layer2: Layer[In2 with Shared, Out2] + ): Layer[In1, Out2] = + new Layer[In1, Out2] { + override def run[T, S](effect: T > (S with In1))(implicit + fl: Flat[T > (S with In1)] + ): T > (S with Out2) = { + val handled1: T > (S with Shared) = layer1.run[T, S](effect) + val handled2: T > (S with Out2) = + layer2.run[T, S](handled1)(Flat.unsafe.unchecked) + handled2 + } + } + + } + + implicit def applyAll2[Out1, Shared]: ChainLayer.Aux[Out1 with Shared, Shared, Out1] = + new ChainLayer[Out1 with Shared, Shared] { + type RemainingOut1 = Out1 + + override def applyLayer[In1, Out2]( + layer1: Layer[In1, Out1 with Shared], + layer2: Layer[Shared, Out2] + ): Layer[In1, Out1 with Out2] = + new Layer[In1, Out1 with Out2] { + override def run[T, S](effect: T > (S with In1))(implicit + fl: Flat[T > (S with In1)] + ): T > (S with Out1 with Out2) = { + val handled1: T > (S with Out1 with Shared) = layer1.run[T, S](effect) + val handled2: T > (S with Out1 with Out2) = + layer2.run[T, S with Out1](handled1)(Flat.unsafe.unchecked) + handled2 + } + } + + } + } + + object ChainLayer extends ChainLayers1 { type Aux[Out1, In2, R] = ChainLayer[Out1, In2] { type RemainingOut1 = R } implicit def simpleChain[Out]: ChainLayer.Aux[Out, Out, Any] = From e9b3e8717fe88733df0788a3d5f9f88ad53b8d33 Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Wed, 13 Dec 2023 08:03:46 -0500 Subject: [PATCH 10/11] Changed order of handled effects in Layer.run --- .../shared/src/main/scala/kyo/aborts.scala | 4 +-- kyo-core/shared/src/main/scala/kyo/envs.scala | 8 ++--- .../shared/src/main/scala/kyo/layers.scala | 30 +++++++++---------- .../shared/src/main/scala/kyo/options.scala | 4 +-- .../shared/src/main/scala/kyo/tries.scala | 4 +-- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/kyo-core/shared/src/main/scala/kyo/aborts.scala b/kyo-core/shared/src/main/scala/kyo/aborts.scala index f96b68a49..2f6e62063 100644 --- a/kyo-core/shared/src/main/scala/kyo/aborts.scala +++ b/kyo-core/shared/src/main/scala/kyo/aborts.scala @@ -96,9 +96,9 @@ object aborts { def layer[Se](handle: E => Nothing > Se): Layer[Aborts[E], Se] = new Layer[Aborts[E], Se] { override def run[T, S]( - effect: T > (S with Aborts[E]) + effect: T > (Aborts[E] with S) )( - implicit flat: Flat[T > (S with Aborts[E])] + implicit flat: Flat[T > (Aborts[E] with S)] ): T > (S with Se) = self.run[T, S](effect)(flat).map { case Left(err) => handle(err) diff --git a/kyo-core/shared/src/main/scala/kyo/envs.scala b/kyo-core/shared/src/main/scala/kyo/envs.scala index 7bd051556..83f9a489b 100644 --- a/kyo-core/shared/src/main/scala/kyo/envs.scala +++ b/kyo-core/shared/src/main/scala/kyo/envs.scala @@ -51,10 +51,10 @@ object envs { def layer[Sd](construct: E > Sd): Layer[Envs[E], Sd] = new Layer[Envs[E], Sd] { - override def run[T, S](effect: T > (S with Envs[E]))(implicit - fl: Flat[T > (S with Envs[E])] - ): T > (S with Sd) = - construct.map(e => self.run[T, S](e)(effect: T > (Envs[E] with S))) + override def run[T, S](effect: T > (Envs[E] with S))(implicit + fl: Flat[T > (Envs[E] with S)] + ): T > (Sd with S) = + construct.map(e => self.run[T, S](e)(effect)) } } diff --git a/kyo-core/shared/src/main/scala/kyo/layers.scala b/kyo-core/shared/src/main/scala/kyo/layers.scala index 5aa7ee054..4241e4a34 100644 --- a/kyo-core/shared/src/main/scala/kyo/layers.scala +++ b/kyo-core/shared/src/main/scala/kyo/layers.scala @@ -3,18 +3,18 @@ package kyo object layers { trait Layer[In, Out] { self => - def run[T, S](effect: T > (S with In))(implicit fl: Flat[T > (S with In)]): T > (S with Out) + def run[T, S](effect: T > (In with S))(implicit fl: Flat[T > (In with S)]): T > (S with Out) final def add[Out1, In1](other: Layer[In1, Out1]): Layer[In with In1, Out with Out1] = new Layer[In with In1, Out with Out1] { override def run[T, S]( - effect: T > (S with In with In1) + effect: T > (In with In1 with S) )( - implicit fl: Flat[T > (S with In with In1)] + implicit fl: Flat[T > (In with In1 with S)] ): T > (S with Out with Out1) = { - val selfRun: T > (S with In1 with Out) = - self.run[T, S with In1](effect: T > (S with In1 with In)) - val otherRun: T > (S with Out with Out1) = + val selfRun: T > (In1 with Out with S) = + self.run[T, S with In1](effect: T > (In1 with In with S)) + val otherRun: T > (Out with Out1 with S) = other.run[T, S with Out](selfRun)(Flat.unsafe.unchecked) otherRun } @@ -46,8 +46,8 @@ object layers { layer2: Layer[In2 with Shared, Out2] ): Layer[In1, Out1 with Out2] = new Layer[In1, Out1 with Out2] { - override def run[T, S](effect: T > (S with In1))(implicit - fl: Flat[T > (S with In1)] + override def run[T, S](effect: T > (In1 with S))(implicit + fl: Flat[T > (In1 with S)] ): T > (S with Out2 with Out1) = { val handled1: T > (S with Out1 with Shared) = layer1.run[T, S](effect) val handled2: T > (S with Out2 with Out1) = @@ -69,8 +69,8 @@ object layers { layer2: Layer[In2 with Shared, Out2] ): Layer[In1, Out2] = new Layer[In1, Out2] { - override def run[T, S](effect: T > (S with In1))(implicit - fl: Flat[T > (S with In1)] + override def run[T, S](effect: T > (In1 with S))(implicit + fl: Flat[T > (In1 with S)] ): T > (S with Out2) = { val handled1: T > (S with Shared) = layer1.run[T, S](effect) val handled2: T > (S with Out2) = @@ -90,8 +90,8 @@ object layers { layer2: Layer[Shared, Out2] ): Layer[In1, Out1 with Out2] = new Layer[In1, Out1 with Out2] { - override def run[T, S](effect: T > (S with In1))(implicit - fl: Flat[T > (S with In1)] + override def run[T, S](effect: T > (In1 with S))(implicit + fl: Flat[T > (In1 with S)] ): T > (S with Out1 with Out2) = { val handled1: T > (S with Out1 with Shared) = layer1.run[T, S](effect) val handled2: T > (S with Out1 with Out2) = @@ -115,10 +115,10 @@ object layers { layer2: Layer[Out, Out2] ): Layer[In1, Any with Out2] = new Layer[In1, Any with Out2] { - override def run[T, S](effect: T > (S with In1))(implicit - fl: Flat[T > (S with In1)] + override def run[T, S](effect: T > (In1 with S))(implicit + fl: Flat[T > (In1 with S)] ): T > (S with Out2) = { - val handled1: T > (S with Out) = layer1.run[T, S](effect) + val handled1: T > (Out with S) = layer1.run[T, S](effect) val handled2: T > (S with Out2) = layer2.run[T, S](handled1)(Flat.unsafe.unchecked) handled2 diff --git a/kyo-core/shared/src/main/scala/kyo/options.scala b/kyo-core/shared/src/main/scala/kyo/options.scala index d0099bc4e..796291e12 100644 --- a/kyo-core/shared/src/main/scala/kyo/options.scala +++ b/kyo-core/shared/src/main/scala/kyo/options.scala @@ -54,8 +54,8 @@ object options { def layer[Se](onEmpty: => Nothing > Se): Layer[Options, Se] = new Layer[Options, Se] { - override def run[T, S](effect: T > (S with Options))(implicit - fl: Flat[T > (S with Options)] + override def run[T, S](effect: T > (Options with S))(implicit + fl: Flat[T > (Options with S)] ): T > (S with Se) = Options.run[T, S](effect).map { case None => onEmpty diff --git a/kyo-core/shared/src/main/scala/kyo/tries.scala b/kyo-core/shared/src/main/scala/kyo/tries.scala index 10c6477c0..de3161244 100644 --- a/kyo-core/shared/src/main/scala/kyo/tries.scala +++ b/kyo-core/shared/src/main/scala/kyo/tries.scala @@ -47,8 +47,8 @@ object tries { def layer[Se](handle: Throwable => Nothing > Se): Layer[Tries, Se] = new Layer[Tries, Se] { - override def run[T, S](effect: T > (S with Tries))(implicit - fl: Flat[T > (S with Tries)] + override def run[T, S](effect: T > (Tries with S))(implicit + fl: Flat[T > (Tries with S)] ): T > (S with Se) = Tries.run[T, S](effect).map { case Failure(exception) => handle(exception) From 4861c9ec4018199693943d6039d332d96a1842d2 Mon Sep 17 00:00:00 2001 From: John Hungerford Date: Wed, 13 Dec 2023 08:22:39 -0500 Subject: [PATCH 11/11] Renamed add to andThen --- kyo-core/shared/src/main/scala/kyo/layers.scala | 2 +- kyo-core/shared/src/test/scala/kyoTest/layersTest.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kyo-core/shared/src/main/scala/kyo/layers.scala b/kyo-core/shared/src/main/scala/kyo/layers.scala index 4241e4a34..8ff8da25b 100644 --- a/kyo-core/shared/src/main/scala/kyo/layers.scala +++ b/kyo-core/shared/src/main/scala/kyo/layers.scala @@ -5,7 +5,7 @@ object layers { trait Layer[In, Out] { self => def run[T, S](effect: T > (In with S))(implicit fl: Flat[T > (In with S)]): T > (S with Out) - final def add[Out1, In1](other: Layer[In1, Out1]): Layer[In with In1, Out with Out1] = + final def andThen[Out1, In1](other: Layer[In1, Out1]): Layer[In with In1, Out with Out1] = new Layer[In with In1, Out with Out1] { override def run[T, S]( effect: T > (In with In1 with S) diff --git a/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala b/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala index 59ba8bb09..d7951ee77 100644 --- a/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala +++ b/kyo-core/shared/src/test/scala/kyoTest/layersTest.scala @@ -22,7 +22,7 @@ class layersTest extends KyoTest { val dep3Layer = Envs[Dep3].layer(Envs[Dep].get.map(v => Dep3(v.dep3))) "Envs layers should be composable and provide multiple dependencies" in { - val layer = (dep1Layer add dep2Layer add dep3Layer) chain depLayer + val layer = (dep1Layer andThen dep2Layer andThen dep3Layer) chain depLayer val effect = for { dep1 <- Envs[Dep1].get @@ -46,7 +46,7 @@ class layersTest extends KyoTest { "Aborts layers should be composable and handle multiple error types" in { val layer = - (stringToTE1Layer add dep1ToTE1Layer add throwableToTE1Layer) chain testError1ToTE2Layer + (stringToTE1Layer andThen dep1ToTE1Layer andThen throwableToTE1Layer) chain testError1ToTE2Layer val effect1 = for { _ <- Aborts[String].fail("string failure")