tags:

views:

119

answers:

3

hi,

if i have a trait:

  trait MyTrait {
    def doSomething = {
      println("boo")
    }
  }

I can add it to a class with "extends":

  class MyClass extends MyTrait {
    ....
  }

or i can add it to a new object when i construct it:

  var o = new MyOtherClass with MyTrait
  o.doSomething

but... can i add it to an existing object?

I am loading some objects using java and JPA (orm), and i would like to add some functionality to them using traits. is it possible?

something like:

  var o = DBHelper.loadMyEntityFromDB(primaryKey);
  o = o with MyTrait //adding trait here, rather than during construction
  o.doSomething

thanks ant

+5  A: 

An existing runtime object in the JVM has a certain size on the heap. Adding a trait to it would mean altering its size on the heap, and changing its signature.

So the only way to go would be to do some kind of transformation at compile time.

Mixin composition in Scala occurs at compile time. What compiler could potentially do is create a wrapper B around an existing object A with the same type that simply forwards all calls to the existing object A, and then mix in a trait T to B. This, however, is not implemented. It is questionable when this would be possible, since the object A could be an instance of a final class, which cannot be extended.

In summary, mixin composition is not possible on existing object instances.

UPDATED:

Related to the smart solution proposed by Googol Shan, and generalizing it to work with any trait, this is as far as I got. The idea is to extract the common mixin functionality in the DynamicMixinCompanion trait. The client should then create a companion object extending DynamicMixinCompanion for each trait he wants to have the dynamic mixin functionality for. This companion object requires defining the anonymous trait object gets created (::).

trait DynamicMixinCompanion[TT] {                                                                    
  implicit def baseObject[OT](o: Mixin[OT]): OT = o.obj                                              

  def ::[OT](o: OT): Mixin[OT] with TT                                                               
  class Mixin[OT] protected[DynamicMixinCompanion](val obj: OT)                                      
}                                                                                                    

trait OtherTrait {                                                                                   
  def traitOperation = println("any trait")                                                          
}                                                                                                    

object OtherTrait extends DynamicMixinCompanion[OtherTrait] {                                        
  def ::[T](o: T) = new Mixin(o) with OtherTrait                                                     
}                                                                                                    

object Main {                                                                                        
  def main(args: Array[String]) {                                                                    
    val a = "some string"                                                                            
    val m = a :: OtherTrait                                                                          
    m.traitOperation                                                                                 
    println(m.length)                                                                                
  }                                                                                                  
}                                                                                                    
axel22
perfect answer, thanks!
Ant Kutschera
+1  A: 

I have a idea for this usage:

//if I had a class like this
final class Test {
  def f = println("foo")
}
trait MyTrait {
  def doSomething = {
    println("boo")
  }
}
object MyTrait {
  implicit def innerObj(o:MixTest) = o.obj

  def ::(o:Test) = new MixTest(o)
  final class MixTest private[MyTrait](val obj:Test) extends MyTrait
}

you can use this trait as below:

import MyTrait._

val a = new Test
val b = a :: MyTrait
b.doSomething
b.f

for your example code:

val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait
o.doSomething

I hope this can help you.

UPDATED

object AnyTrait {
  implicit def innerObj[T](o: MixTest[T]):T = o.obj

  def ::[T](o: T) = new MixTest(o)
  final class MixTest[T] private[AnyTrait](val obj: T) extends MyTrait
}

but this pattern has some restrict, you can't use some implicit helper method that defined already.

val a = new Test
a.f
val b = a :: AnyTrait
b.f1
b.f
val c = "say hello to %s" :: AnyTrait
println(c.intern)  // you can invoke String's method 
println(c.format("MyTrait"))  //WRONG. you can't invoke StringLike's method, though there defined a implicit method in Predef can transform String to StringLike, but implicit restrict one level transform, you can't transform MixTest to String then to StringLike.
c.f1
val d = 1 :: AnyTrait
println(d.toLong)
d.toHexString // WRONG, the same as above
d.f1
Googol Shan
thats perfect - although i dont understand it at all - im a scala beginner :-)
Ant Kutschera
This is a very useful feture, when you defined a method with `implicit`, and import this method in your scope, this method can help you transfer object that specify by the method argument to another object that specify by the method return when you need invoke the latter's method that is not defined at the former.
Googol Shan
Very nice solution, I like it. I wonder how easily could it also be made generic - probably add a generic parameter to `::` in the `MyTrait` object could allow it to work for any type. Could it also be made to work with arbitrary traits which we want to mixin...?
axel22
@axel22 yes, I think it can be made generic like my updated answer. but I can't made it to work with arbitrary trait, I am a newbie for scala.
Googol Shan
Ok, I wrote how it could be made slightly more generic below. Still, it seems to me one can't avoid adding a little bit of boilerplate for each trait object..
axel22
+1  A: 

I usually used a implicit to mix in a new method to an existing object.

See, if I have some code as below:

final class Test {
  def f = "Just a Test"
  ...some other method
}
trait MyTrait {
  def doSomething = {
    println("boo")
  }
}
object HelperObject {
  implicit def innerObj(o:MixTest) = o.obj

  def mixWith(o:Test) = new MixTest(o)
  final class MixTest private[HelperObject](obj:Test) extends MyTrait
}

and then you can use MyTrait method with an already existing object Test.

val a = new Test
import HelperObject._
val b = HelperObject.mixWith(a)
println(b.f)
b.doSomething

in your example, you can use like this:

import HelperObject._
val o = mixWith(DBHelper.loadMyEntityFromDB(primaryKey));
o.doSomething

I am thinking out a prefect syntax to define this HelperObject:

trait MyTrait {
  ..some method
}
object MyTrait {
  implicit def innerObj(o:MixTest) = o.obj

  def ::(o:Test) = new MixTest(o)
  final class MixTest private[MyTrait](obj:Test) extends MyTrait
}
//then you can use it
val a = new Test
val b = a :: MyTrait
b.doSomething
b.f
// for your example
val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait
o.doSomething
Googol Shan