Implicit By-Name Parameters

Implicit parameters can be declared by-name to avoid a divergent inferred expansion. Example:

``````trait Codec[T] {
def write(x: T): Unit
}

given intCodec as Codec[Int] = ???

given optionCodec[T] as Codec[Option[T]] given (ev: => Codec[T]) {
def write(xo: Option[T]) = xo match {
case Some(x) => ev.write(x)
case None =>
}
}

val s = the[Codec[Option[Int]]]

s.write(Some(33))
s.write(None)
``````

As is the case for a normal by-name parameter, the argument for the implicit parameter `ev` is evaluated on demand. In the example above, if the option value `x` is `None`, it is not evaluated at all.

The synthesized argument for an implicit parameter is backed by a local val if this is necessary to prevent an otherwise diverging expansion.

The precise steps for synthesizing an argument for an implicit by-name parameter of type `=> T` are as follows.

1. Create a new given instance of type `T`:

``````given lv as T = ???
``````

where `lv` is an arbitrary fresh name.

2. This given instance is not immediately available as candidate for argument inference (making it immediately available could result in a loop in the synthesized computation). But it becomes available in all nested contexts that look again for an argument to an implicit by-name parameter.

3. If this search succeeds with expression `E`, and `E` contains references to `lv`, replace `E` by

``````{ given lv as T = E; lv }
``````

Otherwise, return `E` unchanged.

In the example above, the definition of `s` would be expanded as follows.

``````val s = the[Test.Codec[Option[Int]]](
optionCodec[Int](intCodec)
)
``````

No local given instance was generated because the synthesized argument is not recursive.

Reference

For more info, see Issue #1998 and the associated Scala SIP.