需要函数参数实现方法 - Scala

时间:2017-08-04 20:51:16

标签: scala interface design-principles

有没有办法可以要求将对象传入函数实现一组核心方法?

例如,我希望能够编写一个求和方法来对实现'+'运算符的任何可迭代对象求和。

我的初步实施如下

trait addable[T <: addable[T]]{
     def +(other: T): T
 }

 def sum[T <: addable[T]](items: Iterable[T]) = 
     if(items.isEmpty) throw new Exception("Can't sum nothing")
     else items.tail.foldRight(items.head)(_+_) 
          //Starst with the first element and adds all other elements to it

现在这种方法有效,但它很笨重。如果我想要一些可以求和的东西,我必须在我想要求和的每个类中明确地实现可添加的[T],更不用说为数值类型和字符串定义一堆显式转换。

有没有办法实现它,看起来像这样?

def sum[T fulfills addable[T]](items: Iterable[T]) = 
    if(items.isEmpty) throw new Exception("Can't sum nothing")
    else items.tail.foldRight(items.head)(_+_) 

或者,是否有一些设计模式消除了对此的需求(我现在正在做的事情似乎只是适配器模式)?

1 个答案:

答案 0 :(得分:1)

执行此类操作的常见模式是使用类型类:http://typelevel.org/cats/typeclasses.html

以下是针对您的用例的Addable类型类的示例实现:

trait Addable[T] {
  def +(a: T, b: T): T
}

// Among other places Scala searches for implicits 
// in the companion objects of the relevant classes.
// Read more in this answer: https://stackoverflow.com/a/5598107
object Addable {

  // Using context bound notation
  def apply[T : Addable]: Addable[T] = implicitly

  // Instance of Addable typeclass for types,
  // that have an instance of the built-in Numeric typeclass
  implicit def numeric[T : Numeric]: Addable[T] = {
    import Numeric.Implicits._
    // This uses Scala 2.12 feature of automatic convertions of lambdas to SAMs
    // You can create an instance of an anonymous subclass in older versions.
    _ + _ 
  }

  // Instance of Addable for all kinds of Iterables, 
  // that adds them element by element (like mathematical vectors)
  implicit def iterable[That, T](implicit
    ev: That <:< IterableLike[T, That], // To compute the element type T from That
    cbf: CanBuildFrom[That, T, That],   // To call `map` method
    add: Addable[T]                     // To add elements of the iterable
  ): Addable[That] =
    (a, b) => (a, b).zipped.map(add.+)
}

以下是使用此sum类型类的Addable方法的示例实现:

def sum[T : Addable](items: Iterable[T]): T = items.
  reduceOption(Addable[T].+).
  getOrElse(throw new Exception("Can't sum nothing"))

使用它的一些结果:

scala> sum(Seq(1.2, 3.4, 5.6))
res0: Double = 10.2

scala> sum(Seq(Vector(1,2), Vector(4,5), Vector(6,7)))
res1: scala.collection.immutable.Vector[Int] = Vector(11, 14)