Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing ExpandoObject in Scala

I am trying to implement C#'s ExpandoObject-like class in Scala. This is how it is supposed to work:

val e = new ExpandoObject
e.name := "Rahul" // This inserts a new field `name` in the object.
println(e.name) // Accessing its value.

Here is what I have tried so far:

implicit def enrichAny[A](underlying: A) = new EnrichedAny(underlying)
class EnrichedAny[A](underlying: A) {
  // ...
  def dyn = new Dyn(underlying.asInstanceOf[AnyRef])
}

class Dyn(x: AnyRef) extends Dynamic {
  def applyDynamic(message: String)(args: Any*) = {
    new Dyn(x.getClass.getMethod(message, 
      args.map(_.asInstanceOf[AnyRef].getClass) : _*).
      invoke(x, args.map(_.asInstanceOf[AnyRef]) : _*))
  }
  def typed[A] = x.asInstanceOf[A]
  override def toString = "Dynamic(" + x + ")"
}

class ExpandoObject extends Dynamic {
  private val fields = mutable.Map.empty[String, Dyn]
  def applyDynamic(message: String)(args: Any*): Dynamic = {
    fields get message match {
      case Some(v) => v
      case None => new Assigner(fields, message).dyn
    }
  }
}

class Assigner(fields: mutable.Map[String, Dyn], message: String) {
  def :=(value: Any): Unit = {
    fields += (message -> value.dyn)
  }
}

When I try to compile this code, I get a StackOverflowError. Please help me get this work. :) Thanks.

like image 516
missingfaktor Avatar asked Oct 20 '11 07:10

missingfaktor


1 Answers

Got it working after some playing around. The solution isn't typesafe though (which in this case does not matter, since the point of this little utility is to work around type system. :-)

trait ExpandoObject extends Dynamic with mutable.Map[String, Any] {
  lazy val fields = mutable.Map.empty[String, Any]
  def -=(k: String): this.type = { fields -= k; this }
  def +=(f: (String, Any)): this.type = { fields += f; this }
  def iterator = fields.iterator
  def get(k: String): Option[Any] = fields get k
  def applyDynamic(message: String)(args: Any*): Any = {
    this.getOrElse(message, new Assigner(this, message))
  }
}

implicit def anyToassigner(a: Any): Assigner = a match {
  case x: Assigner => x
  case _ => sys.error("Not an assigner.")
}

class Assigner(ob: ExpandoObject, message: String) {
  def :=(value: Any): Unit = ob += (message -> value)
}
like image 170
missingfaktor Avatar answered Oct 31 '22 09:10

missingfaktor