This is my first programming blog post, please be gentle in your criticism. 🙂
Emulating C#’s default keyword in Scala
In generic classes and methods, one issue that arises is how to assign a default value to a type parameter A when you do not know in advance whether A is a reference type, a numeric type, or something else. To handle such a situation, C# provides a keyword default, which returns null for reference types, and zero for numeric types. For structs, which are CLR’s value types, default returns an object with all members initialized to their respective default values using the same mechanism.
The following example shows the default keyword in action:
using System;using System.Diagnostics;
class Example { static void Main(string[] args) { Debug.Assert(default(Int32) == 0); Debug.Assert(default(Boolean) == false); Debug.Assert(default(String) == null); }}Scala has a similar feature to get a default value for the given type parameter. Just declare a variable at the class level, annotate it with a type, and put an underscore after the assignment operator. The variable will then be assigned the appropriate default according to its type. (Note: All Scala snippets in this post were typed into the REPL.)
scala> class Test[A] { | var a: A = _ | }defined class Test
scala> new Test[Int].ares3: Int = 0
scala> new Test[Boolean].ares4: Boolean = false
scala> new Test[String].ares5: String = nullHowever, for reasons I do not know, this feature is only available for class fields. It doesn’t work for local variables.
scala> def default[A] = { | var a: A = _ | a | }<console>:8: error: local variables must be initialized var a: A = _ ^This can be worked around easily:
scala> def default[A] = { | class Test { | var a: A = _ | } | (new Test).a | }default: [A]=> A
scala> default[Int]res6: Int = 0
scala> default[Boolean]res7: Boolean = false
scala> default[String]res8: String = nullThus we have successfully emulated the behavior of C#’s default keyword in Scala (except for the structs case, which do not exist on JVM anyway).
Putting it on steroids
Now what do I mean by putting default on steroids? Think about how we could improve the behavior of the defaulting mechanism we built above. Wouldn’t it make more sense for a string to default to an empty string, i.e. ""? Wouldn’t it make more sense for an Option[_] variable to default to None? Similarly, your custom classes could have more appropriate defaults than null. For example, 0.0 + 0.0j would be a suitable default for a Complex object. In the remaining part of this post, we are going to see how Scala’s typeclasses let us build such a default on steroids.
Here is the required machinery for this little utility:
scala> :paste// Entering paste mode (ctrl-D to finish)
trait Default[A] { def value: A}
trait LowPriorityImplicitsForDefault { this: Default.type => implicit def forAnyRef[A](implicit ev: Null <:< A) = Default withValue (null : A)}
object Default extends LowPriorityImplicitsForDefault { def withValue[A](a: A) = new Default[A] { def value = a }
implicit val forBoolean = Default withValue false implicit val forChar = Default withValue ' ' implicit def forNumeric[A](implicit n: Numeric[A]) = Default withValue n.zero implicit val forString = Default withValue "" implicit def forOption[A] = Default withValue (None : Option[A])}
// Exiting paste mode, now interpreting.
defined trait Defaultdefined trait LowPriorityImplicitsForDefaultdefined module DefaultThe trait Default is a typeclass, which is invariant on A for obvious reasons. Its companion object Default contains implicits and implicit suppliers for a few common types. The method forAnyRef is put in a separate trait LowPriorityImplicitsForDefault so that it has a lower precedence than other more specific implicit supplier methods such as forNumeric (we do this to hack around Scala’s implicit lookup rules. Don’t bother if you do not understand this, as it’s beyond the point of this post).
Now all we need to do is define a no-arg method default that picks an appropriate Default implicit from the scope and returns its value.
scala> def default[A : Default] = implicitly[Default[A]].valuedefault: [A](implicit evidence$1: Default[A])AAnd we are done! 🙂
Here is our new default, in action:
scala> default[Int]res0: Int = 0
scala> default[String]res1: String = ""
scala> default[Boolean]res2: Boolean = false
scala> default[Option[Int]]res3: Option[Int] = NoneSay, later on at some point we define a class Complex. How do you supply a Default implicit for it? Easy. Put it in Complex’s companion.
scala> :paste// Entering paste mode (ctrl-D to finish)
case class Complex(real: Double, imaginary: Double)
object Complex { implicit val default = Default withValue Complex(0.0, 0.0)}
// Exiting paste mode, now interpreting.
defined class Complexdefined module Complex
scala> default[Complex]res6: Complex = Complex(0.0,0.0)Cool, isn’t it? 🙂
What happens when there’s no implicit defined for a certain type? Well, in such a case, the method forAnyRef acts as a fallback method, and default returns null.
scala> case class Point(x: Double, y: Double)defined class Point
scala> default[Point]res8: Point = nullThis example shows how Scala’s typeclasses can be immensely useful in cases requiring post hoc polymorphism.
That’s it. Suggestions and criticism are welcome.