Scala/Play, Part 10: Scala 2 Implicit Parameters - What the Compiler Supplies, and Why It Refuses
I kept seeing implicit in Scala code at work. Coming from Java and Spring, I wanted to understand what the compiler was actually doing. Our policy repository gave me a small enough example to follow every argument without introducing a framework.
This lesson uses Scala 2.13.18. We start with multiple parameter lists, let the compiler supply a repository, then deliberately trigger two compilation failures. All policy data is fictional.

First, separate the parameter lists
Part 9 introduced PolicyRepository, whose find method returns Option[PolicySnapshot]. Our caller originally took both inputs together: describe(number, repository). Scala also allows:
def describe(number: String)(repository: PolicyRepository): String =
repository.find(number)
.map(policy => "Found policy " + policy.number)
.getOrElse("Policy not found")
describe("POL-001")(repository)
This is one method with two parameter lists. Both names are available in its body. The complete call still returns String, and I still supply both arguments explicitly. Grouping the dependency separately prepares the next change.
Implicit marks a parameter the compiler may supply
def describe(number: String)
(implicit repository: PolicyRepository): String =
repository.find(number)
.map(policy => "Found policy " + policy.number)
.getOrElse("Policy not found")
The body has not changed. The implicit keyword marks the second parameter list. To make a suitable value available at a call site, our example declares:
implicit val localStore: PolicyRepository =
new InMemoryPolicyRepository(
List(PolicySnapshot("POL-001", 600, 3))
)
describe("POL-001")(localStore) // supplied explicitly
describe("POL-001") // supplied by the compiler
Both calls return "Found policy POL-001". For the second call, the compiler selects localStore and inserts it as the missing argument. The parameter is named repository and the value is named localStore: those names need not match.
The two declarations have complementary roles. The implicit parameter requests a dependency; the implicit val makes this value eligible for search. The choice is resolved during compilation. At runtime, the method receives an ordinary repository object. This is not a Spring container searching for a bean during the call.

An explicit argument still works
val emptyStore: PolicyRepository =
new InMemoryPolicyRepository(List.empty[PolicySnapshot])
describe("POL-001")(emptyStore)
// "Policy not found"
Even with localStore available implicitly, this call uses the explicitly supplied emptyStore. The ordinary val needs no implicit modifier when I pass it myself.
Failure one: no eligible implicit repository
In a separate method, with no eligible implicit repository available, this fails:
val store: PolicyRepository =
new InMemoryPolicyRepository(List.empty[PolicySnapshot])
ImplicitParameterLesson.describe("POL-001")
The compiler does not automatically treat every local variable of the right type as an implicit candidate. Our actual diagnostic was:
could not find implicit value for parameter repository: learning.PolicyRepository
I can pass store explicitly, or mark the intended declaration implicit. Also, a local implicit inside another method's body is not visible at this call site. Scope matters. Scala's broader search rules also consider associated companion objects; this lesson deliberately uses local values and leaves detailed precedence for later.
Failure two: equally suitable candidates
implicit val first: PolicyRepository =
new InMemoryPolicyRepository(
List(PolicySnapshot("POL-001", 600, 3))
)
implicit val second: PolicyRepository =
new InMemoryPolicyRepository(List.empty[PolicySnapshot])
ImplicitParameterLesson.describe("POL-001")
These two values have the same declared type and are in the same scope. The compiler refused the call:
ambiguous implicit values:
both value second of type learning.PolicyRepository
and value first of type learning.PolicyRepository
match expected type learning.PolicyRepository
It does not choose by declaration order or inspect which repository contains the policy. Multiple candidates are not universally ambiguous; suitability and precedence matter. These two are equally suitable in this example.
The working fix selects one:
ImplicitParameterLesson.describe("POL-001")(second)
// "Policy not found"
Alternatively, I can arrange the scope so only the intended candidate remains eligible.

Reproduce the failures without breaking the lab
The failing sources live outside src in examples/implicit-errors. Start sbt from the repository root, then enter:
set Compile / unmanagedSources += baseDirectory.value / "examples" / "implicit-errors" / "MissingRepository.scala"
compile
exit
set changes the build for that session; it does not edit build.sbt. Compile / unmanagedSources lists ordinary main source files, and += adds this one. The slash between path components joins them relative to the project root.
Start a fresh sbt session and substitute AmbiguousRepository.scala to reproduce the second failure. Using the sbt prompt avoided a Windows launcher quoting issue we encountered when passing this expression directly on the command line.
After those expected failures, a fresh normal run exercises the successful examples and fixes:
sbt "runMain learning.PremiumLesson"
Both compiler diagnostics were verified, then the normal lab passed all assertions. The runnable fixes are in ImplicitSearchLesson.scala.
I can now read an omitted argument as a concrete compiler decision, and distinguish an unsuccessful search from a repository returning None. Next: implicit classes, which explain extension-style methods.
Reference: Scala Tour: implicit parameters, using its Scala 2 examples. Our target remains Scala 2.13.18, Play 3 and Apache Pekko.