views:

477

answers:

3

example:

import scala.actors._  
import Actor._  

class BalanceActor[T <: Actor] extends Actor {  
  val workers: Int = 10  

  private lazy val actors = new Array[T](workers)  

  override def start() = {  
    for (i <- 0 to (workers - 1)) {  
      // error below: classtype required but T found  
      actors(i) = new T  
      actors(i).start  
    }  
    super.start()  
  }  
  // error below:  method mailboxSize cannot be accessed in T
  def workerMailboxSizes: List[Int] = (actors map (_.mailboxSize)).toList  
.  
.  
.

Note the second error shows that it knows the actor items are "T"s, but not that the "T" is a subclass of actor, as constrained in the class generic definition.

How can this code be corrected to work (using Scala 2.8)?

+3  A: 

EDIT - apologies, I only just noticed your first error. There is no way of instantiating a T at runtime because the type information is lost when your program is compiled (via type erasure)

You will have to pass in some factory to achieve the construction:

class BalanceActor[T <: Actor](val fac: () => T) extends Actor {
  val workers: Int = 10

  private lazy val actors = new Array[T](workers)

  override def start() = {
    for (i <- 0 to (workers - 1)) {
      actors(i) = fac() //use the factory method to instantiate a T
      actors(i).start
    }
    super.start()
  }
}

This might be used with some actor CalcActor as follows:

val ba = new BalanceActor[CalcActor]( { () => new CalcActor } )
ba.start

As an aside: you can use until instead of to:

val size = 10
0 until size //is equivalent to:
0 to (size -1)
oxbow_lakes
Tried out your suggestion concerning the type specification, but the errors did not change as a result
scaling_out
Sorry - changed my answer - I only saw the 2nd error and didn't notice the `new T` line
oxbow_lakes
Thanks for your suggestions. How would the presence of the factory help with the error calling a method specific to subclasses of Actor (such as the mailboxSize shown in the example)?Thanks for the reminder about the "until," but it is hard to break decades of habit from C and Java... ;-)
scaling_out
The 2nd error is erroneous - fix the first issue and it will be gone. I've edited my answer again
oxbow_lakes
When I say *"erroneous"*, I just mean that the error is there because your `BalanceActor` class has not actually correctly compiled with its generic type information.
oxbow_lakes
+3  A: 

Use Manifest:

class Foo[A](a: A)(implicit m: scala.reflect.Manifest[A]) {
  def create: A = m.erasure.newInstance.asInstanceOf[A]
}

class Bar

var bar1 = new Bar       // prints "bar1: Bar = Bar@321ea24" in console
val foo = new Foo[Bar](bar1)
val bar2 = foo.create    // prints "bar2: Bar = Bar@6ef7cbcc" in console
bar2.isInstanceOf[Bar]   // prints "Boolean = true" in console

BTW, Manifest is undocumented in 2.7.X so use it with care. The same code works in 2.8.0 nightly as well.

Walter Chang
And doesn't work if your A's need constructor parameters
oxbow_lakes
Your way works, as well as oxbow_lakes', for solving the problem of creating new instances of A (T is my original example) But... the error "method mailboxSize cannot be accessed in A" remains. Any idea why?
scaling_out
@Paul It puzzles me as well. "self.mailboxSize" executes successfully in 2.7.5 REPL but raises "method mailboxSize cannot be accessed in scala.actors.Actor" error in 2.8.0. "def workerMailboxSizes" compiles fine in 2.7.5 as well.
Walter Chang
@Paul After looking at the current source code on the trunk for Actor, I think they have removed "mailboxSize" from trait Actor. You will need to use "mailboxSize" in object Actor instead. But that's probably not what you want because it returns the mailbox size of "self" only.
Walter Chang
@Walter Chang: Thanks for checking that out.... I only looked at the javadocs for 2.8 (over at scaladocs.jcraft.com/2.8.0) which don't show the def mailboxSize removed, so I expected it to still be there as in 2.7.x
scaling_out
@Paul They've changed the access level of mailboxSize from public to protected[actors] in revision 17883. Sorry about the confusion.
Walter Chang
+1  A: 

You can't, as mentioned already, instantiate T because of erasure. At run-time, there is no T. This is not like C++'s templates, where the substitution happens are compile-time, and multiple classes are actually compiled, for each variation in actual use.

The manifest solution is interesting, but assumes there is a constructor for T that does not require parameters. You can't assume that.

As for the second problem, the method mailboxSize is protected, so you can't call it on another object. Update: this is true only of Scala 2.8.

Daniel