scalascala-generics

How to pimp `String` as `Iterable[E]`


I am working with Scala's implicit class mechanism and can't get java.lang.String recognized as Iterable[A].

implicit class PStream[E](stream: Iterable[E]) {
    def splitOn(test: Iterable[E] => Boolean): Option[(Iterable[E], Iterable[E])] = {
        ???
    }
}

With just the above definition, IntelliJ and SBT state that ...

Error:(31, 8) value splitOn is not a member of String possible cause: maybe a semicolon is missing before `value splitOn'? .splitOn(s => Character.isWhitespace(s.head))

... when I try this ...

            line: String =>
                val Some((identifier: String, patternn: String)) =
                    line
                        // take the identifier
                        .splitOn(s => Character.isWhitespace(s.head))

Solution

  • That's because Iterable[E] is a Scala trait, and as such a relatively "recent" invention, whereas java.lang.String is a fundamental Java datatype that has been there since version 1.0. from 1995. Obviously, java.lang.String does not implement Iterable[E].

    Moreover, even if there is an implicit conversion from String to Iterable[E], Scala will never attempt more than one implicit conversion on a single expression.

    If you want to pimp String, you have to pass String as a single parameter to your implicit class. The compiler will refuse to build crazy towers of multiple implicit conversions, because it would make the compilation time inacceptable otherwise.


    What you could try instead would be something like this:

    implicit def toPStreamOps[X, E](x: X)(implicit iter: IsIterable[X, E])
    : PStream[X, E] = ???
    

    and then provide a separate

    implicit object StringIsIterableChar 
    extends IsIterable[String, Char] {
      def asIterable(s: String): Iterable[Char] = ???
    }
    

    This would give you pretty much the same functionality, but it would not require an uncontrolled explosion of implicit conversions.