Scala 3 given/using resolution ambiguity in mixed-scope imports
0 reputation · 31 Mar 2025, 23:37 UTC
0 reputation · 31 Mar 2025, 23:37 UTC
Scala 3 introduces given and using to replace the overloaded implicit keyword, aiming to provide clearer intent for dependency injection and type class resolution.
When defining type class instances, the compiler searches local scopes, imported scopes, and companion objects. Uncertainty arises when multiple given instances of the same type are available across different imported packages and the local scope, potentially leading to ambiguous implicit resolution errors.
Given a scenario where a specific type class instance is defined in both a companion object and a separate utility trait imported into the current scope, the priority of resolution needs clarification for Scala 3.x versions.
given instance exists in both the companion object of the type and the immediate local import scope?given instances for the same type?32275 reputation · 01 Apr 2025, 11:14 UTC
In Scala 3, a given that is in scope at the call site — defined locally or brought in by an import — takes precedence over one found in the companion object of the type class or the target type. The companion object is the fallback, searched only when normal scoping finds nothing. So in your scenario, the instance from the imported utility trait wins over the companion-object instance; no ambiguity error is raised between those two tiers.
Resolution happens in two conceptually distinct phases:
import pkg.given and wildcard imports of givens).Phase 1 is searched first. If exactly one candidate is found there, it is selected and phase 2 never runs. This is why a locally imported "override" instance is the idiomatic way to shadow a default instance that lives in a companion object — the same pattern Scala 2 used with implicits.
When two imported scopes each supply a candidate for the same type, both sit in the same priority tier, and the compiler reports an ambiguous-given error rather than picking one. There is no "first import wins" rule. Your options:
import utilA.myOrdering instead of import utilA.given.sort(xs)(using utilB.myOrdering).given that delegates to the preferred instance — local definitions outrank imported ones.The ambiguity error message lists the competing candidates and where they came from, which is the fastest way to confirm which tier each instance occupies. You can also probe resolution directly:
// after your imports
val picked = summon[Ordering[Foo]]
println(picked) // object identity tells you which instance won
Check your exact version with scalac -version. The core rule (normal scope before implicit scope) has been stable across Scala 3.x, but error-message detail and edge cases around using clauses have been refined between minor releases, so treat the compiler output on your installed version as authoritative.
The precedence description above reflects well-established Scala 3 semantics. What I cannot confirm without your code is whether your "imported utility trait" instance is actually a given in scope (e.g., inherited via trait mixin vs. imported via import util.given) — mixin-inherited givens and imported givens can interact differently in corner cases. If the compiler still reports ambiguity after you expect the import to shadow the companion, share the exact import statements and the full error text; that detail determines whether you're hitting a genuine same-tier conflict.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.