I am having a tough time understanding why the Scala compiler is unhappy about this function definition:
def trimNonWordCharacters[T <: Iterable[String]](items: T): T =
items map { _.replaceAll("\\W", "") }
Here is the REPL output:
scala> def trimNonWordCharacters[T <: Iterable[String]](items: T): T =
items map { _.replaceAll("\\W", "") }
<console>:5: error: type mismatch;
found : Iterable[java.lang.String]
required: T
def trimNonWordCharacters[T <: Iterable[String]](items: T): T = items map { _.replaceAll("\\W", "") }
The goal is to pass in any implementation of an Iterable and get the same type of back out. Is this possible?
So methods of generic or nongeneric classes can use generic types as argument and return types as well. Here are examples of those usages: // Not generic methods class GenericClass < T > { // method using generic class parameter type public void T cache ( T entry ) { ... } }
Generic functions are functions declared with one or more generic type parameters. They may be methods in a class or struct , or standalone functions. A single generic declaration implicitly declares a family of functions that differ only in the substitution of a different actual type for the generic type parameter.
Generics allow creating 'type variables' which can be used to create classes, functions & type aliases that don't need to explicitly define the types that they use. Generics makes it easier to write reusable code.
Generic Methods A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.
The map
method on Iterable
returns an Iterable
, so even if T
is a subclass of Iterable
, it's map
method will return Iterable
.
To get better typing, you'd have to write it like this:
import scala.collection.IterableLike
def trimNonWordCharacters[T <: Iterable[String]](items: T with IterableLike[String, T]): T =
items map { _.replaceAll("\\W", "") }
However, that won't work either, because there's no information that let a map on T
to generate another T
. For example, mapping a BitSet
into a String
cannot result in a BitSet
. So we need something else: something that teaches how to build a T
from a T
, where the mapped elements are of type String
. Like this:
import scala.collection.IterableLike
import scala.collection.generic.CanBuildFrom
def trimNonWordCharacters[T <: Iterable[String]]
(items: T with IterableLike[String, T])
(implicit cbf: CanBuildFrom[T, String, T]): T =
items map { _.replaceAll("\\W", "") }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With