Debilski
Debilski

Reputation: 67838

Cast (or rather: ascribe) collection type without specifying type parameter

Say I have a List of Ints

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

Answers (6)

Sagie Davidovich
Sagie Davidovich

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

Debilski
Debilski

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

oxbow_lakes
oxbow_lakes

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

Dan Simon
Dan Simon

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

Alexey Romanov
Alexey Romanov

Reputation: 170733

Write a corresponding method:

def asTraversable[T](x: Traversable[T]) = x

asTraversable(x)

Upvotes: 0

Paolo Falabella
Paolo Falabella

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

Related Questions