如何创建一个在Array和Option上一般工作的函数

时间:2017-04-28 07:30:28

标签: scala

我想创建一个适用于数组作为选项的通用函数:

val numbers = Array(1, 2, 3)
val numberO: Option[Int] = Some(4)

def addOnes(numbers: ???[Int]) = numbers.map(_+1)

addOnes(numbers)
addOnes(numberO)

现在我为每个结构都有一个单独的功能

def addOnesForArray(numbers: Array[Int]) = numbers.map(_+1)
def addOnesForOption(numberO: Option[Int]) = numberO.map(_+1)

addOnesForArray(numbers)
addOnesForOption(numberO)

所以基本上我需要一个Array和Option的超类,它有functor和monad方法map,flatMap,filter,......

2 个答案:

答案 0 :(得分:2)

你可以使用结构类型(又名"鸭子打字"),我们可以说你需要字面意思"带有地图的东西",写成{ def map(): T }。但是,丑陋,b)使用反射,c)很难(注意map(): T只是一个例子;实际上你必须匹配地图的确切签名,CanBuildFrom和所有爵士乐)。

更好的方法是达到scalazcats并使用类别理论概念。只需选择能够完成工作的最不强大的抽象。如果您只需要map,那么它就是一个Functor。如果你想要映射多个参数的(curried)函数,那么它就是一个Applicative。如果您还想要flatMap,那么它就是monad等等。

仿函数示例:

import scalaz._, Scalaz._

def addOne[F[Int]](f: F[Int])(implicit m: Functor[F]) = f.map(_ + 1)

val numbers = Array(1, 2, 3).toList
val numberO = Option(123)

addOne(numbers) // List(2, 3, 4)
addOne(numberO) // Some(124)

您会注意到我必须将您的数组转换为List,因为没有类型实例(我知道)用于数组上的仿函数,应用程序,monad等。但阵列是老式的和不变的,无论如何都不是惯用的Scala。如果你从其他地方获得它们,只需将它们转换为Lists,Vectors等(根据你的用例)并从那时开始使用它们。

答案 1 :(得分:2)

总的来说,我同意@slouc。如果你想让现有的类扩展一些其他特性,你需要类型类。 但在您的特定情况下,由于OptionArray都是Traversable,因此不需要它:

object Example extends App {
  def plus1(data: Traversable[Int]): Traversable[Int] = data.map(x => x + 1)

  println(plus1(Array(1, 2, 3)))
  println(plus1(Some(4)))
}