如何在 Dotty 中使用 given?

How to use given in Dotty?

我正在查看 Contextual Abstractions 页面下的 Dotty 文档,我看到了 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)
  }
}

但是 docs 中的这个示例从未解释如何使用 given。我拉取了测试 Dotty 示例项目并尝试使用它,但我不太了解它。

是新关键字吗?我们进口它吗?还是我遗漏了什么。

下面是使用 given 实例的示例。假设我们要比较两个整数,看看哪个比另一个大。我们可以利用上面已经定义的 intOrd 并编写:

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))

产生:

2 is greater than 1

我们能够做到这一点是因为范围内有一个命名的给定实例,否则编译器会抱怨它没有 Ord[Int].

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

这是一个新关键字,它替换了 Scala 2 中 implicit 定义的特定部分。如果这是 Scala 2,我们会写成:

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

是的,这是一个新关键字,您可以从页面末尾的语法中使用 'given' 看出(第 "Syntax" 节)。它旨在取代 implicit。如果你已经熟悉隐式,我认为 Relationship with Scala 2 Implicits 是很好的开始。

也许比较我们如何在 Scala 2 中使用 implicit 关键字和在 Scala 3 中使用 given 关键字定义类型类会很有启发:

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))