I'm trying to define a reusable trait that expects a value to be in an outer scope. I can define the trait inside the outer scope and it will work, but won't be reusable. When I move the trait to a separate scope, the trait can't access the value and I haven't found a way to declare it as present in the outer scope of the type the trait is being mixed in to.
The closest I have got so far is this:
import javafx.beans.property.ObjectProperty
import akka.actor.{Props, ActorSystem}
import javafx.event.EventHandler
import javafx.stage.{WindowEvent => JWindowEvent}
import scalafx.application.{Platform, JFXApp}
import scalafx.scene.Scene
import scalafx.scene.canvas.Canvas
import scalafx.scene.paint.Color
object MicroServicesApp extends JFXApp {
implicit val system = ActorSystem("system")
val canvas = new Canvas {
width = 1200
height = 900
}
stage = new MicroServicesPrimaryStage with AutomaticMicroServicesWindowCloser {
title.value = "Map Viewer"
scene = new Scene {
fill = Color.LightGreen
content = canvas
}
}
}
class MicroServicesPrimaryStage(implicit val actorSystem: ActorSystem) extends JFXApp.PrimaryStage with MicroServices {
}
/**
* A class enabled with a micro-services actor system.
*/
trait MicroServices {
def actorSystem: ActorSystem
}
/**
* An automatic window closer for a ScalaFX and Akka micro-services application.
*
* When this trait is mixed in to a class with the MicroServices trait and the onCloseRequest property,
* the onCloseRequest property will be initialized with a useful default event handler that shuts down
* the Akka actor system as well as the ScalaFX platform.
*/
trait AutomaticMicroServicesWindowCloser extends MicroServicesWindowCloser {
def onCloseRequest: ObjectProperty[EventHandler[JWindowEvent]]
def onCloseRequest_=(handler: EventHandler[JWindowEvent]): Unit
onCloseRequest = closeRequest()
}
/**
* A window closer for a ScalaFX and Akka micro-services application.
*/
trait MicroServicesWindowCloser extends MicroServices {
def closeRequest(): EventHandler[JWindowEvent] = new EventHandler[JWindowEvent] {
override def handle(e: JWindowEvent)
{
println("... closing application.")
actorSystem.shutdown()
Platform.exit()
}
}
}
It gets pretty close to what I'm after, the only give-away is the need for the client code to declare the value in the outer scope to be implicit. Ideally I would like the client code to mix-in the trait without changing anything else.
In the example I can use 'system' from within 'MicroServicesPrimaryStage', but not from within the mixed-in trait. I think this is because 'system' is in scope but not considered to be defined as a member of 'MicroServicesPrimaryStage'.
I could create an alias for 'system' with a val or a def and make it work that way, but that also means an extra step in modifying the client code. It would be nice if the trait could require a definition for 'system' and be able to find it in the outer scope at the point where the trait is mixed-in.
Is this possible?
Edit 1
These two println statements illustrate the cause of my confusion:
stage = new MicroServicesPrimaryStage with AutomaticMicroServicesWindowCloser {
println(s"val system is accessible from outer scope: $system ...") // compiles
println(s"... but is not mixed-in to MicroServicesPrimaryStage as ${this.system}.") // does not compile
...
I don't think the cake pattern can solve this on its own, because the question is about how the type system interacts with definitions in outer scopes.
Edit 2
SBT file for use with Java 8:
name := "workspace-sbt"
version := "1.0"
scalaVersion := "2.11.4"
resolvers += Opts.resolver.sonatypeSnapshots
libraryDependencies ++= Seq("org.scalatest" % "scalatest_2.11" % "2.2.1" % "test",
"org.scalafx" %% "scalafx" % "8.0.20-R7-SNAPSHOT",
"com.typesafe.akka" %% "akka-actor" % "2.3.7")