Using Scala 3 Given/Implicit Parameters for Clean Dependency Injection
Learn how Scala 3's given/implicit parameters can reduce boilerplate in dependency injection while keeping type safety, with a concrete service‑layer example and tips for debugging implicit resolution.
12 Aug 2025, 10:09 UTC

The problem: manual dependency passing creates noise
In a typical Scala service you might write a constructor that takes a repository and a configuration object. Every time you instantiate the service you have to thread those arguments through layers of code, which adds boilerplate and makes tests harder to set up.
Thesis: let the compiler supply dependencies with given/implicit
Scala 3’s given instances and implicit parameters form a contextual abstraction mechanism. By declaring a given for each dependency and marking service methods with implicit parameters, the compiler can automatically wire the correct instances at the call site.
Defining given instances
A given declares an implicit value that the compiler can look up by type. It lives in an object, a package object, or a class companion.
// src/main/scala/com/example/Repository.scala
package com.example
trait Repository:
def find(id: String): Option[String]
given repo: Repository with
def find(id: String): Option[String] = Some(s"value-for-$id")
// src/main/scala/com/example/Config.scala
package com.example
final case class Config(timeout: Int)
given config: Config = Config(timeout = 5)
Using implicit parameters in a service
The service declares its dependencies as implicit parameters. When you call the method without providing them, the compiler searches for matching given instances in the implicit scope.
// src/main/scala/com/example/Service.scala
package com.example
class Service:
def process(id: String)(implicit r: Repository, c: Config): String =
r.find(id) match
case Some(v) => s"Found $v with timeout ${c.timeout}"
case None => s"Not found for $id"
Worked example: wiring it all together
In Main.scala we create a Service and invoke process. Because the given instances for Repository and Config are in the same package, they are automatically supplied.
// src/main/scala/com/example/Main.scala
package com.example
object Main extends App:
val svc = new Service()
// No explicit arguments needed; the compiler inserts repo and config
val result = svc.process("abc")
println(result)
To build and run the example with sbt:
- Create a new Scala 3 project:
sbt new scala/scala3.g8(choose a name, accept defaults). - Replace the generated source files with the snippets above, keeping the package
com.example. - From the project root run
sbt run. You should see output similar toFound value-for-abc with timeout 5. - To verify that the implicit resolution really matters, delete or comment out the
given repoline and runsbt compile; the compiler will emit an error likecould not find implicit value for parameter r: Repository.
Trade‑off: implicit resolution can become opaque
While the compiler hides the wiring, a missing or ambiguous given leads to errors that can be hard to trace, especially when many implicits are in scope. Overuse also makes it harder for newcomers to see where a value comes from.
Practical ways to keep implicits manageable
- Limit
givendefinitions to narrow scopes (e.g., a package object or a dedicatedDependenciesobject). - Document each implicit with a comment explaining its purpose and lifetime.
- During development enable the flag
-Xlog-implicits(add toscalacOptions) to see how the compiler resolves each implicit. - When debugging, you can temporarily write an explicit call:
svc.process("abc")(repo, config)to confirm the expected instances.
Actionable closing
Start by converting one service constructor to use given/implicit. Verify the build succeeds and the runtime behavior matches the explicit version. Use the implicit‑log flag to watch resolution, and keep a short README that lists the scoped given instances for your module. This approach reduces boilerplate while preserving the type‑safe guarantees that make Scala refactoring safe.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.