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!)