2015-11-29 13 views
5

Questo è qualcosa che ho scoperto in scala e funziona e non riesco a capire perché, qualcuno può spiegare perché funziona?Self referencing a val durante la definizione in scala

In sostanza posso usare il riferimento ad una val mentre sto ancora definendolo (perché i miei altri oggetti/attori prendono come parametro)

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)) 
) 
) 

otterrò un errore di compilazione se I don' t definire esplicitamente il tipo, che ha senso.

risposta

1

Si noti che in questo caso specifico, anche se il codice viene compilato, sarà non comportarsi correttamente in quanto, come suggerito nei commenti sull'altra risposta, il valore di backbone passato negli altri attori è "null".

Questo esempio dimostra questo:

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() 
} 

Questo produce il seguente (più volte, dal momento che la strategia supervisore tenta di riavviare l'attore):

[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) 
1

Nei linguaggi di programmazione funzionale le definizioni ricorsive sono un concetto importante. Pensa ad esempio al classico esempio di definizione del fattoriale.

Per il caso specifico di Scala, una bella spiegazione è stata data in un post all'inizio di questo mese: A variable used in its own definition?

+1

Ma questa non è una definizione ricorsiva, è un riferimento che viene passato prima che sia costruito –

+1

Tuttavia, direi che il commento fatto nell'altro thread da Archeg si applica: "[...] la variabile' a' viene creata quando si digita 'val a: Int', [.. .]". Hai un tipo definito dall'utente e non Int, quindi 'backbone' verrebbe inizializzato a' null' sul lato destro –

+0

@ChristianHirsch Ho la stessa comprensione. Il che significa che 'context.actorOf (F_UserHandler.props (backbone))' riceverà 'backbone' come' null', a meno che non venga valutato pigramente (call-by-name, lazy val etc). Forse un potenziale bug viene introdotto qui tramite 'null'. – tuxdna