Thursday, June 4, 2009

GetAnyClass Tip

I've been having some trouble with calling getClass on generic/primitive types.
It seems that it's a known issue with scala that
wontfix and hence this code won't compile

def convert[A,B](instance : A, toClass : Class[B]) : Option[B] = {
//Fails here with the error below
instance.getClass
...Rest of implementation...
}


(The error):

:6: error: type mismatch;
found : A
required: ?{val getClass: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method any2stringadd in object Predef of type (Any)scala.runtime.StringAdd
and method any2ArrowAssoc in object Predef of type [A](A)ArrowAssoc[A]
are possible conversion functions from A to ?{val getClass: ?}
instance.getClass
^


the problem has a quick fix though suggested by paulp on the #scala irc channel on Freenode:

you can encapsulate that though:
scala> def getAnyClass(x: Any) = x.asInstanceOf[AnyRef].getClass
getAnyClass: (x: Any)java.lang.Class[_ <: java.lang.Object
casting to AnyRef (for this purpose) is a no-op on non-primitives, so...
you could plant that as an implicit if you want so 1.getAnyClass will work.
what this translates to is basically defining a class like this somewhere in your project:

/** Predef for brewmaster, maybe package objects will obsolete this
*/
private[brewmaster] object Brewdef {
implicit def any2GetAnyClass(x : Any) = new GetAnyClass(x)
class GetAnyClass(val x : Any) {
def getAnyClass = x.asInstanceOf[AnyRef].getClass
}
}


then chaning the code above slightly to use the .getAnyClass method it with
Publish Post

import Brewdef._
def convert[A,B](instance : A, toClass : Class[B]) : Option[B] = {
instance.getAnyClass
...
}


voila!
this will also allow things like 5.getAnyClass to compile (.getClass fails)

Monday, June 1, 2009

Immutable Objects and Named Parameters in scala

--Updated--
Jorge Ortiz pointed out that 2.8 is already going to include a copy method in every case class:

Every case class already comes with a "copy" method defined something like:

def copy(x: Int = this.x, y: Int = this.y, z: Int = this.z) = {
Point3d(x, y, z)
}

And used like:

p1.copy(z = 2)

-Jorge Ortiz



Here's a cool way the named parameters feature being added in Scala 2.8 might help with immutable objects. It should help clients avoid errors when copying an immutable class, also it should end up hiding them from certain changes in that class.

//An overly simple example class
case class Point3d(x : Int, val y : Int, val z : Int) {
//Add method like this to make copying immutable classes easier
def copy (x : Option[Int] = None, val y : Option[Int] = None, val z : Option[Int] = None) = {
Point3d(x.getOrElse(this.x), y.getOrElse(this.y), z.getOrElse(this.z))
}
}

//Original class
val p1 = Point3d(1,1,1)

//"Old Way"
//notice every field is copied, this
//gets nasty as the class acquires more than just 3 fields
//Also notice how directly tied to the factory method
//this style is
val pOld = Point3d(p1.x, p1.y, 2)

//"New way"
//here we only care about the part of the
//class that we want to change. The class we are
//changing was refactored and additional fields were
//added to it, we would have to change all
the "Old Way"
//calls, however this call will still work fine.
val pNew = p1.copy(z=Some(2))