How to make method return the same generic as the input?

There's a method in Scala called to which can transform arbitrary collection to another as long as there is typeclass called CanBuildFrom in scope.

import scala.collection.generic.CanBuildFrom
import scala.languageFeature.higherKinds

def genericSplitByComma[S[_]](s: String)(implicit cbf: CanBuildFrom[Nothing, String, S[String]]): S[String] = {
    s.split(",").to[S]
}

genericSplitByComma[Set]("Hello, hello") //Set(Hello,  hello)
genericSplitByComma[List]("Hello, hello") //List(Hello,  hello)
genericSplitByComma[Array]("Hello, hello") //Array(hello, world!)

We don't need to constrain S[_] because this function won't compile if there is no suitable CanBuildFrom in scope. For example, this will fail:

genericSplitByComma[Option]("Hello, hello")

Below will also fail because our type constructor S[_] accepts only one type argument and the map expects two:

genericSplitByComma[Map]("Hello, hello")

As Luis Miguel Mejía Suárez and Dmytro Mitin noticed, there was major refactor in collections in just-released Scala 2.13, so it will work up to Scala 2.12.


@KrzysztofAtłasik's answer works great for Scala 2.12.
This is a solution for 2.13. (Not completely sure if this is the best way).

import scala.collection.Factory
import scala.language.higherKinds

def splitByComma[C[_]](commaDelimited: String)(implicit f: Factory[String, C[String]]): C[String] =
  f.fromSpecific(commaDelimited.split(","))
  // Or, as Dmytro stated, which I have to agree looks better.
  commaDelimited.split(",").to(f)

Which you can use like this:

splitByComma[Array]("hello,world!")
// res: Array[String] = Array(hello, world!)

splitByComma[Set]("hello,world!")
// res: Set[String] = Set(hello, world!)

splitByComma[List]("hello,world!")
// res: List[String] = List(hello, world!)