How to use given in Dotty?
Asked Answered
S

3

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 .

Samul answered 23/12, 2019 at 14:18 Comment(2)
The first link on that page explainsKell
@JoelBerkeley I must be blind , because I don't see it :(Samul
M
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
Maure answered 23/12, 2019 at 15:20 Comment(1)
"we would have written" ... worth mentioning where implicit would go in whichIsBigger esp as given is new?Kell
T
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))
Tribal answered 23/12, 2019 at 15:26 Comment(0)
P
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.

Prefabricate answered 23/12, 2019 at 15:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.