3

I was looking at Dotty docs under Contextual Abstractions page and I saw the Given Instances.

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: Ord[Int] {
  def compare(x: Int, y: Int) =
    if (x < y) -1 else if (x > y) +1 else 0
}

given listOrd[T]: (ord: Ord[T]) => Ord[List[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 compare(xs1, ys1)
  }
}

But this example from docs never explains how to use given. I pulled the test Dotty example project and try yo use it, but I don't quite understand it.

Is it a new keyword ? Do we import it ? Or am I missing something .

stefanobaghino
  • 11,253
  • 4
  • 35
  • 63
amer
  • 1,528
  • 1
  • 14
  • 22

3 Answers3

7

Here's an example of using the given instance. Let's say we want to compare two integers, and see which is bigger than the other. We can leverage the already defined intOrd above and write:

def whichIsBigger[T](x: T, y: T)(given ord: Ord[T]): String = {
  ord.compare(x, y) match {
    case -1 => s"$x is less than $y"
    case 0 => s"$x and $y are equal"
    case 1 => s"$x is greater than $y"
  }
}

println(whichIsBigger(2, 1))

Which yields:

2 is greater than 1

We were able to do this because there was a named given instance in scope, otherwise, the compiler would have complained it doesn't have an Ord[Int].

Is it a new keyword ? Do we import it ? Or am I missing something.

It is a new keyword, one which replaces a specific part of implicit definition in Scala 2. If this was Scala 2, we would have written:

implicit val intOrd: Ord[Int] = new Ord[Int] {
  def compare(x: Int, y: Int) =
    if (x < y) -1 else if (x > y) 1 else 0
}

def whichIsBigger[T](x: T, y: T)(implicit ord: Ord[T]): String
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 1
    "we would have written" ... worth mentioning where `implicit` would go in `whichIsBigger` esp as `given` is new? – joel Dec 23 '19 at 16:18
4

Perhaps it would be instructive to compare how we might define a typeclass using implicit keyword in Scala 2 versus using given keyword in Scala 3:

Scala 2

trait Semigroup[A] {
  def combine(x: A, y: A): A
}

object Semigroup {
  def combine[A: Semigroup](x: A, y: A) = implicitly[Semigroup[A]].combine(x,y)

  implicit val intSemigroup: Semigroup[Int] = new Semigroup[Int] {
    def combine(x: Int, y: Int) = x + y
  }

  implicit val quxSemigroup: Semigroup[Qux] = new Semigroup[Qux] {
    def combine(x: Qux, y: Qux) = Qux(x.a + y.a)
  }
}

case class Qux(a: Int)

Semigroup.combine(41, 1)
Semigroup.combine(Qux(41), Qux(1))

Scala 3

trait Semigroup[A] {
  def combine(x: A, y: A): A
}

object Semigroup {
  def combine[A](x: A, y: A)(given Semigroup[A]) = summon.combine(x,y)

  given intSemigroup: Semigroup[Int] {
    def combine(x: Int, y: Int) = x + y
  }

  given quxSemigroup: Semigroup[Qux] {
    def combine(x: Qux, y: Qux) = Qux(x.a + y.a)
  }
}

case class Qux(a: Int)

Semigroup.combine(41, 1))
Semigroup.combine(Qux(41), Qux(1))
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
3

Yes, it's a new keyword, as you can tell from 'given' being used in the grammar at the end of the page (section "Syntax"). It is intended to replace implicit. If you are already familiar with implicits, I think Relationship with Scala 2 Implicits is good to start with.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487