Ralph
Ralph

Reputation: 32284

Using Scala 2.10 implicit classes to convert to "built-in" standard library classes

I am trying to use the new Scala 2.10 implicit class mechanism to convert a java.sql.ResultSet to a scala.collection.immutable.Stream. In Scala 2.9 I use the following code, which works:

/**
 * Implicitly convert a ResultSet to a Stream[ResultSet]. The Stream can then be
 * traversed using the usual methods map, filter, etc.
 *
 * @param resultSet the Result to convert
 * @return a Stream wrapped around the ResultSet
 */
implicit def resultSet2Stream(resultSet: ResultSet): Stream[ResultSet] = {
  if (resultSet.next) Stream.cons(resultSet, resultSet2Stream(resultSet))
  else {
    resultSet.close()
    Stream.empty
  }
}

I can then use it like this:

val resultSet = statement.executeQuery("SELECT * FROM foo")
resultSet.map {
  row => /* ... */
}

The implicit class that I came up with looks like this:

/**
 * Implicitly convert a ResultSet to a Stream[ResultSet]. The Stream can then be
 * traversed using the usual map, filter, etc.
 */
implicit class ResultSetStream(val row: ResultSet)
  extends AnyVal {
  def toStream: Stream[ResultSet] = {
    if (row.next) Stream.cons(row, row.toStream)
    else {
      row.close()
      Stream.empty
    }
  }
}

However, now I must call toStream on the ResultSet, which sort of defeats the "implicit" part:

val resultSet = statement.executeQuery("SELECT * FROM foo")
resultSet.toStream.map {
  row => /* ... */
}

What am I doing wrong?

Should I still be using the implicit def and import scala.language.implicitConversions to avoid the "features" warning?

UPDATE

Here is an alternative solution that converts the ResultSet into a scala.collection.Iterator (only Scala 2.10+):

/*
 * Treat a java.sql.ResultSet as an Iterator, allowing operations like filter,
 * map, etc.
 *
 * Sample usage:
 * val resultSet = statement.executeQuery("...")
 * resultSet.map {
 *   resultSet =>
 *   // ...
 * }
 */
implicit class ResultSetIterator(resultSet: ResultSet)
extends Iterator[ResultSet] {
  def hasNext: Boolean = resultSet.next()
  def next() = resultSet
}

Upvotes: 4

Views: 607

Answers (1)

Régis Jean-Gilles
Régis Jean-Gilles

Reputation: 32719

I don't see a reason here to use implicit classes. Stick to you first version. Implicit classes are mainly useful (as in "concise") to add methods to existing types (the so called "enrich my library" pattern). It is just syntactic sugar for a wrapper class and an implicit conversion to this class.

But here you are just converting (implicitly) from one preexisting type to another preexisting type. There is no need to define a new class at all (let alone an implicit class).

In your case, you could make it work using implicit classes by making ResultSetStream extend Stream and implementing as a proxy to toStream. But that would really a lot fo trouble for nothing.

Upvotes: 4

Related Questions