tags:

views:

854

answers:

3

I m new to scala. When learning Actor, I tried to extend it to save one line of def:

import scala.actors.Actor
import Actor._
class Actoo(actoo: =>Unit) extends Actor {
    def act() {actoo}
}
object run extends Application {
    /* 
    // this one runs well
    val a = new Actor {
        def act() {
            receive { case 1 => println("1") }
        }
    }
    */
    val a = new Actoo {
        receive { case 1 => println("1") }
    }
    a.start
    a ! 1
}

Then the exception trace looks like this:

java.lang.AssertionError: assertion failed: receive from channel belonging to other actor
    at scala.Predef$.assert(Predef.scala:92)
    at scala.actors.Actor$class.receive(Actor.scala:424)
    at Actoo.receive(actoo.scala:3)
    at run$$anon$1.<init>(actoo.scala:16)
    at run$.<init>(actoo.scala:15)
    at run$.<clinit>(actoo.scala)
    at run.main(actoo.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at scala.tools.nsc.ObjectRunner$$anonfun$run$1.apply(ObjectRunner.scala:75)
    at scala.tools.nsc.ObjectRunner$.withContextClassLoader(ObjectRunner.scala:49)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:74)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:154)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

There may be many alternatives which can do the same thing, but it's better for me to know the reason why the code above doesn't work. thx ~

+5  A: 

It's pretty easy. This behavior is not due to actors library, in fact. The piece of code

val a = new Actoo {
        receive { case 1 => println("1") }
    }

gets interpreted by a compiler as "create new instance of Actoo" with an initialization body receive {...} and val actoo becomes equal to (). Thus, your code is equivivalent to

val a = new Actoo() {
        receive { case 1 => println("1") }
    }

In order to fix the code, you need to write

val a = new Actoo ({
        receive { case 1 => println("1") }
    })
Alexander Azarov
it works! thank you very much!
+1  A: 

Your are actually trying to receive from mailbox on the current native thread (native threads are Actors as well).

Witness the following:

Welcome to Scala version 2.7.5.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_13).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.actors.Actor._
import scala.actors.Actor._

scala> self ! 123

scala> receive { case x => println(x) }
123

Now, what you want to accomplish is in the library already (Actor.actor):

val a = actor {
  receive { case x => println(x) }
}
// no need to start a 
a ! 123

BTW, it is a very bad idea to extend Application. Use def main(args: Array[String]) instead.

Walter Chang
thanks for your answer!Is there something bad to extend Application? I thought they were equivalent when args were not used ...
Please see http://scala-blogs.org/2008/07/application-trait-considered-harmful.html and http://blogs.sun.com/navi/en_US/entry/scala_puzzlers_part_2 for a good explanation of pitfalls when using Application trait. However, I do believe, if you know the pitfalls you are absolutely free to use Application trait for quick testing purposes
Alexander Azarov
Walter, if you look at the stacktrace, you will see the receive is called upon Actoo object, not self. But the call happens in Actoo constructor, when the actor is not yet started.
Alexander Azarov
+5  A: 

There's also an actor method in the Actor singleton that does what you want. It even calls start for you automatically.

import scala.actors.Actor
import Actor._

val a = actor {
  receive { case 1 => println("1") }
}

a ! 1
Jorge Ortiz