Your method arraysToPoints is superfluous. You could use a view bound on the array argument for your method f and add the conversion to the companion object of Point, like so:
object Point {
implicit def arrayToPoint[A](a: Array[A])(implicit view: A => Double): Point =
Point(a(0), a(1))
}
case class Point(x: Double, y: Double)
def f[P](coords: Array[P])(implicit view: P => Point): Unit = coords.foreach { p =>
println(p: Point)
}
f(Array(Point(1, 2), Point(2, 3)))
f(Array(Array(1.0, 2.0), Array(3.0, 4.0)))
f(Array(Array(1, 2), Array(3, 4)))
In order to allow both arrays of Int and Double to be covered, I have used a second view bound on the arrayToPoint method. Otherwise you would need two separate conversion methods for Array[Int] and Array[Double].
You can read this definition of f as, "take an array of elements of a type P which can be viewed as type Point". One spot where the compiler looks for such views is the companion object of the target type, thus object Point. This is a good place for implicit methods.
The second possibility would be to use the magnet pattern. Instead of converting point by point with a view in f, you would create a single wrapper object at once. This is a bit prettier and for large arrays minimises the penalty on direct Array[Double] arguments (because you instantiate the wrapper once, but then do not need to call the view function any more). arrayToPoint is used whenever the array element type A once again can be viewed as a Double. This is true for Double itself of course, but also for Int which can be seen as a Double though what Scala calls numeric widening (e.g., you can say val x: Double = 33 and the integer 33 is implicitly "widened" to a double).
object Points {
implicit def direct(a: Array[Point]): Points =
new Points {
val peer = a
}
implicit def indirect[A](a: Array[Array[A]])(implicit view: A => Double): Points =
new Points {
lazy val peer = a.map { c => Point(c(0), c(1)) }
}
}
trait Points {
def peer: Array[Point]
}
def f(coords: Points): Unit = coords.peer.foreach(println)
This looks in the companion object for an implicit method from the argument type to the special magnet type Points. I use lazy val for the non-direct arrays so that we might save the actual conversion action if the peer method is not called.