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



我是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类中;就我的理解而言,这相当于做桔子.incDiameter(5(,所以类似于Array[Fruit].incDia直径(5(的东西,但由于incDiameter没有在Array类中声明,所以应该在哪里声明它才能使第三行工作?我的直觉是,可能有一种方法可以修改函数如何应用于我们自己类的可迭代项,因此示例的第三行实际上被转换为oranges.map(_.incDiameter(5((,但我从未在的任何地方看到过这种情况

扩展方法:

implicit class OrangesOps(val oranges: Array[Orange]) extends AnyVal {
def incDiameter(by: Int): Array[Orange] = oranges.map(_.incDiameter(5))
}

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

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

然后,如果您可以提供一个隐式证明,证明您的类型有一个类型类的实例,那么您就可以使用incDiameter方法(只要实例和扩展方法都将被定义/导入到作用域中(

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

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

最新更新