8

This is something that I found out in scala and it works and I can't figure out why, can anyone explain why this works?

Essentially I can use the reference to a val while I am still defining it (because my other objects/actors take it in as a parameter)

val backbone: ActorRef = context.actorOf(
  F_BackBone.props(
    context.actorOf(F_PictureHandler.props(backbone)), 
    context.actorOf(F_UserHandler.props(backbone)), 
    context.actorOf(F_PageProfileHandler.props(backbone))
  )
)

I will get a compiler error if I don't explicitly define the type, which makes sense.

Nyavro
  • 8,806
  • 2
  • 26
  • 33

2 Answers2

2

Note that in this specific case, although the code compiles, it will not behave correctly since, as suggested in comments on the other answer, the value of backbone passed into the other actors is 'null'.

This example demonstrates this:

import akka.actor.{Props, Actor, ActorRef, ActorSystem}

class SenderReceiver(sendTo:ActorRef) extends Actor{

  override def preStart(): Unit = {
    self ! "Start"
  }

  def receive = {
    case "Start" => sendTo ! "Hello"
    case "Hello" => println("Received Hello")
  }
}

object SenderReceiver {
  def props(sendTo:ActorRef):Props = Props(new SenderReceiver(sendTo))
}

object Example extends App {

  val system = ActorSystem()

  val actor: ActorRef = system.actorOf(SenderReceiver.props(actor))
  system.awaitTermination()
}

This yields the following (repeatedly, since the supervisor strategy tries to restart the actor):

[info] [ERROR] [12/01/2015 09:47:04.543] [default-akka.actor.default-dispatcher-9] [akka://default/user/$a] null
[info] java.lang.NullPointerException
[info]  at SenderReceiver$$anonfun$receive$1.applyOrElse(example.scala:10)
[info]  at akka.actor.Actor$class.aroundReceive(Actor.scala:467)
[info]  at SenderReceiver.aroundReceive(example.scala:3)
[info]  at akka.actor.ActorCell.receiveMessage(ActorCell.scala:516)
[info]  at akka.actor.ActorCell.invoke(ActorCell.scala:487)
[info]  at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:238)
[info]  at akka.dispatch.Mailbox.run(Mailbox.scala:220)
[info]  at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:397)
[info]  at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
[info]  at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
[info]  at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
[info]  at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
mattinbits
  • 10,370
  • 1
  • 26
  • 35
1

In functional programming languages recursive definitions are an important concept. Think for instance of the classic example of defining the factorial.

For the specific case of Scala, a very nice explanation was given in a post earlier this month: A variable used in its own definition?

Community
  • 1
  • 1
Christian Hirsch
  • 1,996
  • 12
  • 16
  • 1
    But this isn't a recursive definition, it's a reference getting passed before it's constructed – Brandon Ross Pollack Nov 29 '15 at 15:13
  • 1
    Nevertheless, I would say that the comment made in the other thread by Archeg applies: "[...] variable `a` is created when you type `val a: Int`, [...]". You have a user-defined type and not Int, so `backbone` would be initialized to `null` in the right-hand side – Christian Hirsch Nov 29 '15 at 16:08
  • @ChristianHirsch I have same understanding. Which means, `context.actorOf(F_UserHandler.props(backbone))` will receive `backbone` as `null`, unless it is being evaluated lazily ( call-by-name, lazy val etc ). Perhaps a potential bug is being introduced here via `null`. – tuxdna Dec 01 '15 at 05:11