This is ericpony's blog

Thursday, February 20, 2014

Scala: Duck Typing

Scala offers a functionality known as structural typing, which allows to set a behaviour very similar to what dynamic languages allow to do when they support duck typing. The main difference between the two is that structural typing is a type-safe, static-typed implementation checked up at compile time. This means that you can create a method that receives an expected duck. At compile time, it would be checked that anything passed to the method can actually quack like a duck.
We give some examples below to show how structural typing is used in Scala.

Example 1

case class ForInt(x: Int) {
  def myPrint() = println("Int: " + x)
}
case class ForDouble(x: Double) {
  def myPrint() = println("Double: " + x)
}
case class ForString(x: String) {
  def myPrint() = println("String: '" + x + "'")
}
val forInt: ForInt = new ForInt(3)
val forString: ForString = new ForString("Hello World")
val forDouble: ForDouble = new ForDouble(3.14159)
/* 
* This is like defining a "Printable" interface in Java. With structural typing,
* however, a type doesn't have to implement a Printable interface explicitly 
* in order to be recognized as printable. Scala compiler will check that for you.
*/
type T = { def myPrint(): Unit } 
val list: List[T] = List[T](forInt, forString, forDouble)
list.foreach(_.myPrint())

Example 2

def using[A <: {def close(): Unit}, B](res: A)(op: A => B):
  B = try op(res) finally res.close
 
def writeToFile(path: String, data: String): Unit = 
  using(new BufferedWriter(new OutputStreamWriter(
    new FileOutputStream(path), "UTF-8")))(_.write(data))
 
def appendToFile(path: String, data: String): Unit =
  using(new BufferedWriter(new OutputStreamWriter(
    new FileOutputStream(path, true), "UTF-8")))(_.write(data))
  
def printToFile(path: String)(op: PrintWriter => Unit): Unit = 
  using(new PrintWriter(new BufferedWriter(new OutputStreamWriter(
    new FileOutputStream(path), "UTF-8"))))(op)

Technical remarks. In Scala 2.10 or later you should either add
import scala.language.reflectiveCalls
to any classes that use structural typing, or add the -language:reflectiveCalls option to the scalac command line, otherwise you will get a compile warning. For more info on why, see SIP 18 for more details.

Calling methods in structural types is made using reflection, which means it will be much slower than a normal method call using a trait. For that reason, structural types in Scala should generally be avoided. Use them when you need them and in situations where the performance impact won't be too significant. See here for arguments on other points against the usefulness of structural typing in Scala. Another concern of duck typing is that it is not type safe. You can do something similar to duck typing in a type-safe and performant manner is to use type classes. Unfortunately, type classes cannot be realized very well in a JVM language, as Java bytecode is type-erasure. See this paper for a proposal arguably better than reflection to do structural typing in JVM languages.

No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...