Getting started
Add the dependency
The library is published as four artifacts. Pick the ones you need:
libraryDependencies ++= Seq(
"org.atnos" %% "registry" % "0.1.5", // core
"org.atnos" %% "registry-scalacheck" % "0.1.5", // optional
"org.atnos" %% "registry-cats" % "0.1.5", // optional
"org.atnos" %% "registry-circe" % "0.1.5" // optional
)
Only registry is required; the others are integrations.
Your first registry
Entries are added with the strict prepend operator +:. The compiler checks
that every input on the left is produced by something on the right.
import registry.*
case class Host(value: String)
case class Port(value: Int)
case class DbConfig(host: Host, port: Port)
case class Db(config: DbConfig)
case class App(db: Db)
val r =
fun[App] +:
fun[Db] +:
fun[DbConfig] +:
value(Host("localhost")) +:
value(Port(5432))
fun[T] registers T’s primary constructor as a builder for T. value(x)
registers a constant. The order matters only for resolution precedence (LIFO):
entries near the head of the chain win when several entries produce the same
type.
Building a value
Two extractors:
val app = r.make[App]
// app: App = App(Db(DbConfig(host = Host("localhost"), port = Port(5432))))
make[T] resolves at runtime. If T is missing or there is a cycle, it
throws with a typed error message.
makeSafe[T] checks at compile time that T is produced and that every
declared input can be produced by the registry:
val safeApp = r.makeSafe[App]
// safeApp: App = App(
// Db(DbConfig(host = Host("localhost"), port = Port(5432)))
// )
What’s next
- Concepts:
+:vs*:vs-:, resolution rules,makevsmakeSafe, memoization. - Modules: ScalaCheck, cats, circe.
- Recipes: overriding by context, sealed traits, effectful wiring.