5

Let's say I have some code that uses List

def processList(input: List[Int]): List[Int]

I want to replace list with other collection types, like Vector.

Is there a way to define a type constructor so that I can write something like

type SomeCollection[_] = List[_]

def processList(input: SomeCollection[Int]): SomeCollection[Int]

Now I have written processList in terms of SomeCollection. To change SomeCollection to Vector, I just change the type alias, and everywhere in the codebase where I use SomeCollection, I now use Vector. Like so:

type SomeCollection[_] = Vector[_]

def processList(input: SomeCollection[Int]): SomeCollection[Int]

This way, I only need to change the codebase in one place instead of everywhere.

I do not want to write

type SomeIntCollection = List[Int]

because I have connected the collection to Int type.

Is there a way?

Allen Han
  • 1,163
  • 7
  • 16
  • 5
    `type SomeCollection[X] = List[X]` done. However, let me tell you that this sounds like a bad idea. Changing from **List** to **Vector** should mean different performance characteristics and thus, different code. – Luis Miguel Mejía Suárez Oct 08 '19 at 20:29
  • 2
    There are good reasons to use type aliases, but this isn't one of them. To the compiler, `List` and `SomeCollection` will be the same type, so it will let you use operations that are specific to `List`s, and your code may not compile when changing to `Vector` in that case. You may want to use a common parent of `List` and `Vector`, like `Iterable`, if that is enough for your use-case. Otherwise, you should like at advanced solutions like cats, as suggested in the accepted answer. – francoisr Oct 09 '19 at 06:48
  • I thought I would respond with a description of my use case. The idea is to be able to quickly do a performance comparison on the same codebase using different collections types. The performance comparison in question is between a SeqView and an IndexedSeq. I had recently been reading a book on functional programming and learned about laziness. I decided to try it out. It turns out the difference in performance is about 300x in running time. IndexedSeq won by a huge margin, probably because of better cache behavior. – Allen Han Oct 09 '19 at 17:01

1 Answers1

7

You're pretty close, this can be done along the lines of

type SomeCollection[A] = List[A]

def processList(input: SomeCollection[Int]): SomeCollection[Int] = input.map(_+1)

However, there's better ways to describe the abstraction. In the cats library, there are a variety of typeclasses designed to abstract over the type of operation you want to perform. The above with cats would look something like

import cats._
import cats.implicits._

def process[F[_]: Functor](input: F[Int]): F[Int] = input.map(_+1)

It doesn't lock you into a specific underlying collection, so you're free to use whatever makes the most sense at the call site.

Ethan
  • 821
  • 4
  • 12