Функция, которая в общем случае принимает тип и возвращает тот же тип

Мне сложно понять, почему компилятор Scala недоволен определением этой функции:

def trimNonWordCharacters[T <: Iterable[String]](items: T): T =
     items map { _.replaceAll("\\W", "") }

Вот результат REPL:

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", "") }

Цель состоит в том, чтобы передать любую реализацию Iterable и получить тот же тип возврата. Возможно ли это?

Ответы

Ответ 1

Метод map на Iterable возвращает Iterable, поэтому даже если T является подклассом Iterable, метод map возвращает Iterable.

Чтобы лучше набирать текст, вам нужно написать его следующим образом:

import scala.collection.IterableLike
def trimNonWordCharacters[T <: Iterable[String]](items: T with IterableLike[String, T]): T =
     items map { _.replaceAll("\\W", "") }

Однако это тоже не сработает, потому что нет информации, позволяющей карте на T генерировать другую T. Например, отображение a BitSet в String не может привести к BitSet. Поэтому нам нужно что-то еще: что-то, что учит, как построить T из T, где отображаемые элементы имеют тип String. Вот так:

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", "") }

Ответ 2

[Ввод как ответ, а не комментарий, потому что код в комментариях не форматируется должным образом]

@Даниэль, спасибо за объяснение, я также счел это полезным. Поскольку Iterable происходит от IterableLike, похоже, что это работает и немного компактнее:

import scala.collection.IterableLike
import scala.collection.generic.CanBuildFrom
def trimNonWordCharacters[T <: IterableLike[String, T]]
 (items: T)
 (implicit cbf: CanBuildFrom[T, String, T]): T =
 items map { _.replaceAll("\\W", "") }