Reputation: 67838
Say I have a List
of Int
s
scala> val list = List(1, 2, 3, 4, 5)
list: List[Int] = List(1, 2, 3, 4, 5)
Of course, Scala is smart enough to return the correct type for me. Now, considering, I’m not interested in the specific features of List
but rather want to have a more general (super-) type, say, Traversable
. Obviously, I can specify it:
scala> val trav = list: Traversable[Int]
trav: Traversable[Int] = List(1, 2, 3, 4, 5)
but this means that I also have to repeat the type parameter.
This won’t work
scala> list : Traversable
<console>:9: error: type Traversable takes type parameters
list : Traversable
^
And these two examples delete the type parameter information altogether
scala> list : Traversable[T forSome {type T}]
res2: Traversable[T forSome { type T }] = List(1, 2, 3, 4, 5)
scala> list : Traversable[_]
res3: Traversable[Any] = List(1, 2, 3, 4, 5)
Is there a way to get a Traversable[Int]
without having to type Int
?
Upvotes: 1
Views: 302
Reputation: 588
In scala 2.10 it's much simpler, since the introduction of .to[Col[_]]
, which is part of scala.collection.TraversableLike
here's the definition:
def to[Col[_]](implicit cbf : scala.collection.generic.CanBuildFrom[scala.Nothing, A, Col[A]]) : Col[A]
So you can basically do:
scala> List(1, 2, 3, 4, 5).to[Vector]
res0: Vector[Int] = Vector(1, 2, 3, 4, 5)
scala> List(1, 2, 3, 4, 5).to[Set]
res1: Set[Int] = Set(5, 1, 2, 3, 4)
Upvotes: 0
Reputation: 67838
While oxbow_lakes gave a great solution on how to solve this problem for arbitrary combinations, he inspired me on how to do this for the case where Traversable
is a super type of List
. (This wasn’t originally specified in the question, though.)
class CastToSuperType[F[_], A](f: F[A]) {
def as[G[_]](implicit ev: F[A] <:< G[A]): G[A] = f: G[A]
}
implicit def implCastToSuperType[F[_], A](f: F[A]) = new CastToSuperType(f)
scala> List(1, 2, 3).as[Traversable]
res0: Traversable[Int] = List(1, 2, 3)
I suspect, types with an arbitrary number of type parameters might need type lambdas, though.
Upvotes: 1
Reputation: 134270
What you are asking for is basically a polymorphic function; or a function over higher-kinded types. You might define such a mapping between types * -> * as follows:
scala> trait ~>[F[_], G[_]] { def map[A](f: F[A]): G[A] }
defined trait $tilde$greater
You'd need an implicit instance of course
scala> implicit object ListIsTrav extends (List ~> Traversable) {
| def map[A](l: List[A]): Traversable[A] = l
| }
defined module ListIsTrav
Now add a converter for types of the form * -> *
scala> class Homomorphic[F[_], A](f: F[A]){
| def as[G[_]](implicit ev: F ~> G): G[A] = ev map f
| }
defined class Homomorphic
scala> implicit def Type_Is_Homomorphic[F[_], A](f: F[A]) = new Homomorphic(f)
Type_Is_Homomorphic: [F[_], A](f: F[A])Homomorphic[F,A]
And now use it:
scala> List(1, 2, 3).as[Traversable]
res0: Traversable[Int] = List(1, 2, 3)
The pain here is the exponential explosion of implicit instances of the List ~> Traversable
sort. It's not that usable in practice.
Upvotes: 4
Reputation: 13127
Collection classes have conversion methods for most of the generalized traits
scala> List(1,2,3).toTraversable
res0: Traversable[Int] = List(1, 2, 3)
scala> List(1,2,3).toIterable
res1: Iterable[Int] = List(1, 2, 3)
scala> List(1,2,3).toIndexedSeq
res2: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)
Upvotes: 0
Reputation: 170733
Write a corresponding method:
def asTraversable[T](x: Traversable[T]) = x
asTraversable(x)
Upvotes: 0
Reputation: 25844
This works but is a lot of work to avoid typing Int... Maybe someone can come up with a more concise trick based on this.
scala> def traversableId[T](t: Traversable[T])= t
traversableId: [T](t: Traversable[T])Traversable[T]
scala> traversableId(list)
res1: Traversable[Int] = List(1, 2, 3, 4, 5)
Upvotes: 2