DSL:将函数应用于特定类型集合的每个元素的快捷方式

DSL : shortcut to apply a function on every element of a collection of a specific type

我是 scala 的新手,我正在尝试利用它的强大功能来创建一个简单的 DSL。我发现在使用 DSL 时可以创建类似下面示例的内容,我想知道它是如何实现的:

val oranges = Array.tabulate(2)(i => Orange(i+10)) //Orange(diameter)
// Oranges of diameter 10 and 11
oranges incDiameter 5
// Oranges in the array now have diameter 15 and 16

我不明白的是我们如何直接在数组上应用 incDiameter 函数,因为我们不能将函数 incDiameter 添加到 Array scala class;根据我的理解,它等同于做 oranges.incDiameter(5),所以类似于 Array[Fruit].incDiameter(5),但是由于 incDiameter 没有在数组 class 中声明,它应该在哪里声明命令第三行工作? 我的直觉是,可能有一种方法可以修改函数如何应用于我们自己的 classes 的可迭代对象,因此示例的第 3 行实际上转换为 oranges.map(_.incDiameter(5) ) 但我从未在任何地方看到过

扩展方法:

implicit class OrangesOps(val oranges: Array[Orange]) extends AnyVal {

  def incDiameter(by: Int): Array[Orange] = oranges.map(_.incDiameter(5))
}

如果您希望该扩展方法适用于您可以证明适用的任何类型,您可以使用类型 classes:

trait IncreasableDiameter[A] {

  def incDiamater(what: A)(by: Int): A
}

implicit class DiamatersOps[A](val what: A) extends AnyVal {

  def incDiameter(by: Int)(implicit increasable: IncreasableDiameter[A]): A =
    increasable.incDiamater(what)(by)
}

然后,如果你能提供一个隐式证明,证明你的类型有一个 class 类型的实例,你就可以使用 incDiameter 方法(只要两个实例和扩展方法将 defined/imported 纳入范围)

implicit val orangesIncreasable: IncreasableDiameter[Orange] =
  new IncreasableDiameter[Orange] {
    def incDiamater(what: Orange)(by: Int): Orange = what.incDiamater(by)
  }

implicit def arrayIncreasable[A](
  implicit increasable: IncreasableDiameter[A]
): IncreasableDiameter[Array[A]] = new IncreasableDiameter[Array[A]] {

  def incDiamater(what: Array[A])(by: Int): Array[A] = what.map(_.incDiamater(by))
}

这样您就可以调用此操作:

val orange: Orange = ...
orange.incDiameter(5) // oranges built-in method
Array(orange).incDiameter(5) // no build in method, but extension method can be used
                             // because we can produce type class for Array[Orange]
Array(Array(orange)).incDiameter(5) // similar to above, we can create
                                    // type class for Array[Array[Orange]]

根据您需要的灵活性,您可以使用简单的扩展方法,或者 - 如果您希望能够将它们与更多类型一起使用并生成实现 基于一些原则——类型 class。对于初学者,请尝试前者,只有当您需要可扩展性时才选择后者。如果您想了解更多,请了解更多:隐式、扩展方法和类型 classes.