Dotty Documentation

0.18.0-bin-SNAPSHOT

Given Instances

Edit this page on GitHub

Given instances (or, simply, "givens") define "canonical" values of certain types that serve for synthesizing arguments to given clauses. Example:

trait Ord[T] {
  def compare(x: T, y: T): Int
  def (x: T) < (y: T) = compare(x, y) < 0
  def (x: T) > (y: T) = compare(x, y) > 0
}

given IntOrd as Ord[Int] {
  def compare(x: Int, y: Int) =
    if (x < y) -1 else if (x > y) +1 else 0
}

given ListOrd[T] as Ord[List[T]] given (ord: Ord[T]) {

  def compare(xs: List[T], ys: List[T]): Int = (xs, ys) match {
    case (Nil, Nil) => 0
    case (Nil, _) => -1
    case (_, Nil) => +1
    case (x :: xs1, y :: ys1) =>
      val fst = ord.compare(x, y)
      if (fst != 0) fst else xs1.compareTo(ys1)
  }
}

This code defines a trait Ord with two given instances. IntOrd defines a given for the type Ord[Int] whereas ListOrd[T] defines givens for Ord[List[T]] for all types T that come with a given instance for Ord[T] themselves. The given (ord: Ord[T]) clause in ListOrd defines an implicit parameter. Given clauses are further explained in the next section.

Anonymous Given Instances

The name of a given instance can be left out. So the definitions of the last section can also be expressed like this:

given as Ord[Int] { ... }
given [T] as Ord[List[T]] given Ord[T] { ... }

If the name of a given is missing, the compiler will synthesize a name from the type(s) in the as clause.

Alias Givens

An alias can be used to define a given instance that is equal to some expression. E.g.:

given global as ExecutionContext = new ForkJoinPool()

This creates a given global of type ExecutionContext that resolves to the right hand side new ForkJoinPool(). The first time global is accessed, a new ForkJoinPool is created, which is then returned for this and all subsequent accesses to global.

Alias givens can be anonymous, e.g.

given as Position = enclosingTree.position
given as Context given (outer: Context) = outer.withOwner(currentOwner)

An alias given can have type parameters and given clauses just like any other given instance, but it can only implement a single type.

Given Instance Initialization

A given instance without type parameters or given clause is initialized on-demand, the first time it is accessed. It is not required to ensure safe publication, which means that different threads might create different instances for the same given definition. If a given definition has type parameters or a given clause, a fresh instance is created for each reference.

Syntax

Here is the new syntax of given instances, seen as a delta from the standard context free syntax of Scala 3.

TmplDef          ::=  ...
                  |   ‘given’ GivenDef
GivenDef         ::=  [id] [DefTypeParamClause] GivenBody
GivenBody        ::=  [‘as’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
                   |  ‘as’ Type {GivenParamClause} ‘=’ Expr
ConstrApp        ::=  SimpleConstrApp
                   |  ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
SimpleConstrApp  ::=  AnnotType {ArgumentExprs}
GivenParamClause ::=  ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes)
GivenTypes       ::=  AnnotType {‘,’ AnnotType}

The identifier id can be omitted only if either the as part or the template body is present. If the as part is missing, the template body must define at least one extension method.