kitlangton/zio-magic
{ "createdAt": "2021-01-17T01:30:10Z", "defaultBranch": "master", "description": "Construct ZLayers automagically (w/ helpful compile-time errors)", "fullName": "kitlangton/zio-magic", "homepage": "", "language": "Scala", "name": "zio-magic", "pushedAt": "2024-01-22T18:02:19Z", "stargazersCount": 253, "topics": [], "updatedAt": "2025-11-10T20:05:01Z", "url": "https://github.com/kitlangton/zio-magic"}🪄 zio-magic
Section titled “🪄 zio-magic”[![Release Artifacts][Badge-SonatypeReleases]][Link-SonatypeReleases] [![Snapshot Artifacts][Badge-SonatypeSnapshots]][Link-SonatypeSnapshots]
ANNOUNCEMENT: This library is officially included as a first-class feature of ZIO 2.0 😊
Construct ZLayers automagically, with friendly compile-time hints!
libraryDependencies += "io.github.kitlangton" %% "zio-magic" % "0.3.12"What’s all this then?
Section titled “What’s all this then?”// Given a dependency graph (Cake needs Chocolate & Flour, which in turn need Spoon)*//// Cake// / \// Chocolate Flour// | |// Spoon Spoon//// *Not an actual recipe.import zio._import zio.magic._
def run(args: List[String]): URIO[ZEnv, ExitCode] = {
// An effect requiring Cake and Console. Yum! val program: URIO[Console with Cake, Unit] = Cake.isDelicious.flatMap { bool => console.putStrLn(s"Cake is delicious: $bool") }
// The old way val manually: ULayer[Cake with Console] = ((Spoon.live >>> Flour.live) ++ (Spoon.live >>> Chocolate.live)) >>> Cake.live ++ Console.live
// The magical way (The order doesn't matter) val magically: UIO[Unit] = program.inject( Cake.live, Flour.live, Chocolate.live, Spoon.live, Console.live )
magically.exitCode}And if you leave something off, a compile time clue!
val magically: UIO[Unit] = program.inject( Cake.live, //Flour.live, <-- Oops Chocolate.live, Spoon.live, Console.live ) ZLayer Wiring Error
> provide zio.magic.Example.Flour.Service> for Cake.liveVersus leaving out a dependency when manually constructing your layer…
val manually: ULayer[Cake with Console] = (Flour.live ++ (Spoon.live >>> Chocolate.live)) >>> Cake.live ++ Console.live // ^ A Spoon is missing here!type mismatch; found : zio.ZLayer[zio.magic.Example.Spoon.Spoon with Any,Nothing,zio.magic.Example.Cake.Cake with zio.console.Console] (which expands to) zio.ZLayer[zio.Has[zio.magic.Example.Spoon.Service] with Any,Nothing,zio.Has[zio.magic.Example.Cake.Service] with zio.Has[zio.console.Console.Service]] required: zio.ULayer[zio.magic.Example.Cake.Cake with zio.console.Console] (which expands to) zio.ZLayer[Any,Nothing,zio.Has[zio.magic.Example.Cake.Service] with zio.Has[zio.console.Console.Service]] ((Flour.live) ++ (Spoon.live >>> Chocolate.live)) >>> Cake.live ++ Console.liveYou can also directly construct a ZLayer (However you must annotate the call to ZLayer.wire[LikeThis], because macros).
val layer = ZLayer.wire[Flour with Console]!(Console.live, Flour.live, Spoon.live)To construct URLayer[In, Out] use ZLayer.wireSome[In, Out] this way:
val layer = ZLayer.wireSome[CommonEnv, Flour with Console]!(Console.live, Flour.live, Spoon.live)Alternatively you can provide environment partially with injectSome[Rest]!(l1, l2, l3) - similarly to .provideSomeLayer.
There’s also .injectCustom for which behaves similarly to .provideCustomLayer, only it also provides ZEnv.any to all transitive dependencies.
val program: URIO[Console with Car, Unit] = ???
val carLayer: URLayer[Blocking with Wheels, Car] = ???val wheelLayer: ULayer[Wheels] = ???
// The ZEnv you use later will provide both Blocking to carLayer and Console to the programval provided: URIO[ZEnv, Unit] = program.injectCustom(carLayer, wheelLayer)inject, injectCustom, injectSome, injectShared, injectCustomShared and injectSomeShared all work for zio-test’s Spec.
Debug!
Section titled “Debug!”Try ZLayer.wireDebug[Cake] or ZLayer.wireSomeDebug[Blocking with Console, Cake] to print out a pretty tree! Ooh la la!
ZLayer Wiring Graph===================
◉ Cake.live ├─◑ Flour.live │ ├─◑ Spoon.live │ │ ╰─◑ Blocking.live │ ╰─◑ Console.live ╰─◑ Berries.live ╰─◑ Spoon.live ╰─◑ Blocking.liveLet me know if you can think of any helpful variants, and I’ll give ‘em a whirl!
[Badge-SonatypeReleases] !: https://img.shields.io/nexus/r/https/oss.sonatype.org/io.github.kitlangton/zio-magic_2.13.svg “Sonatype Releases” [Badge-SonatypeSnapshots] !: https://img.shields.io/nexus/s/https/oss.sonatype.org/io.github.kitlangton/zio-magic_2.13.svg “Sonatype Snapshots” [Link-SonatypeSnapshots] !: https://oss.sonatype.org/content/repositories/snapshots/io/github/kitlangton/zio-magic_2.13/ “Sonatype Snapshots” [Link-SonatypeReleases] !: https://oss.sonatype.org/content/repositories/releases/io/github/kitlangton/zio-magic_2.13/ “Sonatype Releases”