Fun with Scala Implicits

On this current project of mine, I wanted to provide operations like adding / removing a field or adding / removing multiple fields to a BSON Object (from the BSON library – org.bson.BSONObject) along with operations like toBytes on it. Instead of creating a utils class that contains all these behaviors, I resorted to Scala’s Implicits. With that I can then write more readable code like:

import DocumentOperations._

val document: BSONObject = ...

// add a field with value
document + ("field" ,  value)

// remove a field from document
document - "field"

val otherDocument: BSONObject = ...
// add document
val aggregate = document ++ otherDocument

// remove existing fields from document present in otherDocument
val remaining = document -- otherDocument

If we compiled the previous code without importing the class DocumentOperations, Scala compiler will shout that methods +, -, ++ and — do not exist on document. With the import and the definition of DocumentOperations class present, we tell Scala to convert BSONObject to DocumentOperations and that helps us accomplish the above operations. As this conversion takes place without explicit syntax or method call, it is called Implicit Type Conversion. With this it appears as if the original BSONObject has this behavior, while in reality a type conversion from BSONObject -> DocumentOperations takes place. Using such an Implicit Type conversion we can augment things using our own domain-specific syntax.

Scala compiler automatically applies all the conversions in the current and in the imported scope. When we imported the DocumentOperations, we automatically got implicit def apply(document: BSONObject) inside object DocumentOperations in our scope and the conversion of BSONObject to DocumentOperations was effected.

import org.bson.{BasicBSONEncoder, BSONObject}
import scala.collection.JavaConverters._

class DocumentOperations private (document: BSONObject) {
  def + [T] (field: String, value: T): BSONObject = {
    document.put(field, value)
    document
  }

  def - (name: String): BSONObject = {
    document.removeField(name)
    document
  }

  def ++ (fields: BSONObject) : BSONObject = {
    document.putAll(fields)
    document
  }

  def -- (fields: BSONObject) : BSONObject = {
    fields.toMap.asScala.foreach { case(index, value) =>
      val name = value.asInstanceOf[String]
      document.removeField(name)
    }
    document
  }

  def toBytes: Array[Byte] =
    DocumentOperations.ENCODER.encode(document)
}

object DocumentOperations {
  private val ENCODER = new BasicBSONEncoder()
  implicit def apply(document: BSONObject) =
    new DocumentOperations(document)
}

Below is another example where I got rid of a factory to create a more visually appealing DSL to explicitly show the wiring. Before Using Scala Implicits, the code would look something like this:

import PipesFactory._

val requestPipe = createRequestPipe(client, server)
val responsePipe = createResponsePipe(client, server)
...
val duplexPipe = createDuplexPipe(client, server)
...

// PipesFactory.scala
object PipesFactory {
  def createRequestPipe(from: Socket, to: Socket) = {
    val fromIn = from.getInputStream
    val toOut = to.getOutputStream
    new SimplexPipe("-->", fromIn, toOut)
  }

  def createResponsePipe(from: Socket, to: Socket) = {
    val toIn = to.getInputStream
    val fromOut = from.getOutputStream
    new SimplexPipe("<--", toIn, fromOut)
  }

  def createDuplexPipe(client: Socket, server: Socket) = {
    val requestPipe = createRequestPipe(client, server)
    val responsePipe = createResponsePipe(client, server)
    new DuplexPipe(requestPipe, responsePipe)
  }
}

After Refactoring to Scala Implicits and renaming PipeFactory to SocketConnector, the code looks like this:

import SocketConnector._

val requestPipe = client ==> server
val responsePipe = client <== server
...
val duplexPipe = client <==> server
...
//SocketConnector.scala
import java.net.Socket

class SocketConnector private (source: Socket) {

  def <==> (target: Socket) =
    new DuplexPipe(==> (target), <== (target))

  def ==> (target: Socket) = {
    val srcIn = source.getInputStream
    val tgtOut = target.getOutputStream
    new SimplexPipe("==>", srcIn, tgtOut)
  }

  def <== (target: Socket) = {
    val tgtIn = target.getInputStream
    val srcOut = source.getOutputStream
    new SimplexPipe("<==", tgtIn, srcOut, interceptable)
  }
}

object SocketConnector {
  implicit def apply(source: Socket) = new SocketConnector(source)
}

So that was some fun with Scala’s Implicit Type conversion.

Dhaval Dalal

Advertisements

One thought on “Fun with Scala Implicits

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s