This thread is the SIP Committee’s request for comments on a proposal to revise implicit parameters in Scala. It forms part of a set of proposals that are collected under “Reference/Contextual Abstractions” in the Dotty Docs. Each of these proposals will get a separate discussion thread here. The relevant sections for the current proposal are Inferable Parameters and parts of Relationship with Scala-2 Implicits. Here’s a summary of the proposal that excerpts from these sections.

Note: To channel discussions, we are going to deal with individual “Contextual Abstractions” proposals in separate threads. But it would be good to read and absorb all proposals there together before starting to discuss.

Inferable Parameters

Functional programming tends to express most dependencies as simple function parameterization.

This is clean and powerful, but it sometimes leads to functions that take many parameters and

call trees where the same value is passed over and over again in long call chains to many

functions. Inferable parameters can help here since they enable the compiler to synthesize

repetitive arguments instead of the programmer having to write them explicitly.

For example, a maximum function that works for any arguments for which an ordering exists can be defined as follows:

def max[T](x: T, y: T) given (ord: Ord[T]): T = if (ord.compare(x, y) < 1) y else x

Here, ord is an inferable parameter. Inferable parameters are introduced with a given clause.

The max method can be applied as follows:

max(2, 3) given IntOrd

The given IntOrd part provides the IntOrd instance as an argument for the ord parameter. But the point of inferable parameters is that this argument can also be left out (and it usually is). So the following applications are equally valid:

max(2, 3) max(List(1, 2, 3), Nil)

Anonymous Inferable Parameters

In many situations, the name of an inferable parameter of a method need not be

mentioned explicitly at all, since it is only used in synthesized arguments for

other inferable parameters. In that case one can avoid defining a parameter name

and just provide its type. Example:

def maximum[T](xs: List[T]) given Ord[T]: T = xs.reduceLeft(max)

maximum takes an inferable parameter of type Ord only to pass it on as an

inferred argument to max . The name of the parameter is left out.

Generally, inferable parameters may be given either as a parameter list

(p_1: T_1, ..., p_n: T_n) or as a sequence of types, separated by commas.

Inferring Complex Arguments

Here are two other methods that have an inferable parameter of type Ord[T] :

def descending[T] given (asc: Ord[T]): Ord[T] = new Ord[T] { def compare(x: T, y: T) = asc.compare(y, x) } def minimum[T](xs: List[T]) given Ord[T] = maximum(xs) given descending

The minimum method’s right hand side passes descending as an explicit argument to maximum(xs) . With this setup, the following calls are all well-formed, and they all normalize to the last one:

minimum(xs) maximum(xs) given descending maximum(xs) given (descending given ListOrd) maximum(xs) given (descending given (ListOrd given IntOrd))

Mixing Inferable And Normal Parameters

Inferable parameters can be freely mixed with normal parameters.

An inferable parameter may be followed by a normal parameter and vice versa.

There can be several inferable parameter lists in a definition. Example:

def f given (u: Universe) (x: u.T) given Context = ... implied global for Universe { type T = String ... } implied ctx for Context { ... }

Then the following calls are all valid (and normalize to the last one)

f("abc") (f given global)("abc") f("abc") given ctx (f given global)("abc") given ctx

Syntax

Here is the new syntax of parameters and arguments seen as a delta from the standard context free syntax of Scala 3.

ClsParamClause ::= ... | ‘given’ (‘(’ ClsParams ‘)’ | GivenTypes) DefParamClause ::= ... | GivenParamClause GivenParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | GivenTypes) GivenTypes ::= AnnotType {‘,’ AnnotType} InfixExpr ::= ... | InfixExpr ‘given’ (InfixExpr | ParArgumentExprs)

Relationship with current Implicits

The new inferable parameter syntax with given corresponds largely to Scala-2’s implicit parameters. E.g.

def max[T](x: T, y: T) given (ord: Ord[T]): T

would be written

def max[T](x: T, y: T)(implicit ord: Ord[T]): T

in Scala 2. The main difference concerns applications of such parameters. Explicit arguments to inferable parameters must be written using given , mirroring the definition syntax. E.g, max(2, 3) given IntOrd . Scala 2 uses normal applications max(2, 3)(IntOrd) instead. The Scala 2 syntax has some inherent ambiguities and restrictions which are overcome by the new syntax. For instance, multiple implicit parameter lists are not available in the old syntax. They require instead a complex emulation using auxiliary objects in the “Aux” pattern.

Context Bounds

Context bounds are the same in both language versions. They expand to the respective forms of implicit parameters.

Note: To ease migration, context bounds in Dotty map for a limited time to old-style implicit parameters for which arguments can be passed either with given or with a normal application. Once old-style implicits are deprecated, context bounds will map to given parameters instead.

Implementation Status and Timeline

The Dotty implementation implements both old-style implicit parameters and new-style given parameters. In fact, support for Scala-2’s implicits is an essential part of the common language subset between 2.13/2.14 and Dotty. Migration to the new abstractions will be supported by making automatic rewritings available.

It is planned to deprecate and phase out old-style implicit parameters in a version following Scala 3.0. The precise timeline will depend on adoption patterns.