2

I have a function of the following form:

def tTest[T](it1 : TraversableOnce[T], it2 : Option[TraversableOnce[T]] =  None)
  (implicit frac: Fractional[T]) = {
...
}

My intent is to call it like so:

tTest( Array(1.0,2,3,4), Option(Array(1.0,8,7)) )

and also sometimes like:

tTest( Array(1.0,2,3,4) )

The second one works fine, but when I try to call the first, I get the following:

scala:14: type mismatch;
found   : Option[Array[Double]]
required: Option[TraversableOnce[?]]

[EDIT] this code works fine:

tTest( Array(1.0,2,3,4), Option(Array(1.0,8,7).toTraversable) )

My question is this: What is the relationship between Array and TraversableOnce in Scala? Intuitively, I would think the above should work, since an array is in fact traversable at least once.

On a practical note, what is the simplest way to get this to work for Arrays, Sets, Streams, and any other data structure that is traversable once?

joshuaar
  • 87
  • 1
  • 7

2 Answers2

3

Firstly, your code doesn't work because type inference is confused. Specify type perameter explicitly:

tTest[Double]( Array(1.0,2,3,4), Option(Array(1.0,8,7)) )

Array[T] is Scala's representation for Java's T[]. Array is not a Scala Collection/Traversable. It is implicitly converted to one but it is not an instance of a Traversable.

To see a nice overview of Scala collections and their relatioship, check this: http://docs.scala-lang.org/overviews/collections/overview.html

You can also use implicit conversion:

import scala.language.implicitConversions
implicit def conv[T](a: Option[Array[T]]): Option[Traversable[T]] = a.map(_.toTraversable)

Personaly I usually use Iterable[T] if the only thing I need to do is to simply run through all elements, but it's not optimal in general. Here is some reasoning about it: Scala: What is the difference between Traversable and Iterable traits in Scala collections?

Community
  • 1
  • 1
Rado Buransky
  • 3,252
  • 18
  • 25
  • Actually the code works fine with T if I turn the second array into a traversable. The issue seems to be with wrapping the second array in Option. Also I'd like to avoid specifying double, float etc. for the sake of being generic. I edited to show what I mean. – joshuaar Mar 05 '14 at 04:50
  • I have updated my answer. Array is a very special structure. You can say that it's not a Scala type, but Java. If you can, use List instead. – Rado Buransky Mar 05 '14 at 05:06
  • Ahh, you are correct. List works just fine. Its a shame, because the only reason I wrote it this way is so it would work for combinations of Arrays, Lists, Streams or whatever. – joshuaar Mar 05 '14 at 05:29
  • I've added one more thing. You can implement implicit conversion for Array beacuse that's the problematic type. – Rado Buransky Mar 05 '14 at 05:47
1

Here is another solution:

scala> def tTest[T, C[_]](it1: C[T], it2: Option[C[T]] = None)
     |                   (implicit frac: math.Fractional[T], ev: C[T] => TraversableOnce[T]): TraversableOnce[T] = ev(it1)
tTest: [T, C[_]](it1: C[T], it2: Option[C[T]])(implicit frac: scala.math.Fractional[T], implicit ev: C[T] => scala.collection.TraversableOnce[T])scala.collection.TraversableOnce[T]

scala> tTest( Array(1.0,2,3,4), Option(Array(1.0,8,7)) )
res0: scala.collection.TraversableOnce[Double] = [D(1.0, 2.0, 3.0, 4.0)

scala> tTest( Array(1.0,2,3,4) )
res1: scala.collection.TraversableOnce[Double] = [D(1.0, 2.0, 3.0, 4.0)

EDIT The above solution doesn't work for following case:

scala> tTest(Array(1.2), Option(List(2.0)))
<console>:9: error: inferred kinds of the type arguments (Double,Object) do not conform to the expected kinds of the type parameters (type T,type C).
Object's type parameters do not match type C's expected parameters:
class Object has no type parameters, but type C has one
              tTest(Array(1.2), Option(List(2.0)))
              ^
<console>:9: error: type mismatch;
 found   : Array[Double]
 required: C[T]
              tTest(Array(1.2), Option(List(2.0)))

So, here is a more complex and flexible one:

scala> import math.Fractional
import math.Fractional

scala> import collection.{TraversableOnce => TO}
import collection.{TraversableOnce=>TO}

scala> def tTest2[T, C1[_], C2[_]](i1: C1[T], i2: Option[C2[T]])
     |                            (implicit fc: Fractional[T],
     |                                      e1: C1[T] => TO[T],
     |                                      e2: C2[T] => TO[T]): TO[T] = e1(i1)


scala> tTest2(Array(1.2, 2.3), Option(List(2.3)))
res5: scala.collection.TraversableOnce[Double] = [D(1.2, 2.3)

scala> tTest2(Array(1.2, 2.3), Option(Array(2.3)))
res6: scala.collection.TraversableOnce[Double] = [D(1.2, 2.3)
Eastsun
  • 18,526
  • 6
  • 57
  • 81