3

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")
dk14
  • 22,206
  • 4
  • 51
  • 88
richj
  • 7,499
  • 3
  • 32
  • 50
  • Not sure whether this help or not, but you can declare constraint on where the trait can be declared, like: `trait Tax { this: Trade =>` (from: http://debasishg.blogspot.hu/2010/02/scala-self-type-annotations-for.html) – Gábor Bakos Dec 25 '14 at 15:15
  • Thanks for the suggestion. I experimented with self-types and chose to extend 'MicroServices' directly rather than constrain the eventual type to do so. It might be a better starting point, but I think both approaches lead to the compiler needing an explicit definition of 'actorSystem' in the eventual type ('MicroServicesPrimaryStage'). I think it's interesting that the members of the outer object are not considered members of the class that's mixed together inside the outer object's scope. – richj Dec 25 '14 at 15:39
  • I found this answer that explains the difference and perhaps the self-type more clearly expresses the intention: http://stackoverflow.com/questions/7250374/what-is-more-scala-idiomatic-trait-traita-extends-traitb-or-trait-traita-self?rq=1 – richj Dec 25 '14 at 16:01
  • 1
    Self-contained code is helpful (or a build.sbt because I'm lazy), and updating the question to reflect the chatter in comments is helpful. – som-snytt Dec 26 '14 at 07:17

3 Answers3

0

You are mistaken:

"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'."

This is not true. You can certainly use super class members as definitions for abstract members of mixed in traits. Consider this:

trait Foo { 
    def foo: String 
    def printFoo = println(foo)
}

class FooBar(val foo)

object FooBar {
    def main(argv: Array[String]) = new FooBar("foo") with Foo printFoo
}

This compiles and will print "foo" if run. Is it not what you are trying to do?

Dima
  • 39,570
  • 6
  • 44
  • 70
  • This is a different case. I'm attempting to use a value from an outer scope as a definition without redefining it. If you move your 'val foo' definition out of 'class Bar' and into 'object FooBar' your example will be closer to what I'm trying to achieve - but this won't compile which is why I've had to use implicits. – richj Dec 25 '14 at 23:03
  • Ah, ok I see, what you are saying. Well, if your problem is just with using implicits, it does not have to be. Why not simply pass `system` to the class constructor as a parameter? I updated my answer to illustrate this. – Dima Dec 25 '14 at 23:25
  • I am effectively doing as you say, but (almost) hiding the presence of the parameter from the client code using implicits. It's pretty amazing that Scala lets me get this far but I'm still looking for a "cleaner" way to achieve the same result from the point of view of the client code. – richj Dec 26 '14 at 10:41
  • 1
    Yes, indeed you are "hiding" it ... I am just not sure it is a good thing. Do you like magic? – Dima Dec 26 '14 at 11:02
0

Sorry if I, too, am missing something.

This is just the classic charlotte cake pattern.

Or, maybe you're asking for a fruit cake, with an extra surprise in the next layer. (Maybe a King's cake is a better metaphor.)

package cakesample

// something useful
trait Something {
  def thing: String
}

// a trait requiring something
trait Needy { _: Something =>
  def theThingIs: String = thing
}

// another trait that uses something
trait User { _: Something =>
  def use: String = thing * 2
}

// fruit cake fixings
case class Widget(w: String)

trait WidgetFramework {
  // used by the framework
  def widget: Widget

  trait WidgetCog {
    def run() = Console println s"Running ${widget.w}"
  }
}

// sample usage
object Test extends App with Something with Needy with User with WidgetFramework {
  // normal cake, a charlotte
  def thing = "hello, world"
  Console println s"$theThingIs: $use"

  // a fruit cake

  // define a widget
  val widget = Widget("my widget")

  // to be used by an object implementing a trait
  object client extends WidgetCog

  client.run()
}

I don't know why it should be yellow, except yellow is funnier than pound in this context. (Update: charlotte is more technically correct; but in the spirit of the season, a fruit cake is maybe what you're after.)

som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • I thought that you were on to something, but transferring the inheritance to the outer object creates different problems. I started looking at this problem because I found the default closing behaviour of the ScalaFX window a bit raw and I thought this would be easy to fix with a one line change to the client code, with a little bit of reusable "library" code behind the scenes. The approach with implicits was OK but I thought there must be a better way. Self-types are an alternative to trait inheritance, but both approaches are equivalent when it comes to [re]usability in client code. – richj Dec 26 '14 at 23:31
0

Maybe that's what you're looking for:

scala> abstract class Aaaa(implicit val a: Int)
defined class Aaaa

scala> class Kkk extends Aaaa
<console>:9: error: could not find implicit value for parameter a: Int
       class Kkk extends Aaaa
                         ^

scala> implicit val a = 5
a: Int = 5

scala> class Kkk extends Aaaa
defined class Kkk

scala> new Kkk
res12: Kkk = Kkk@1a79ef3

scala> res12.a
res13: Int = 5

Let's imagine, that Int is an ActorSystem)

This value will be accessible from both Kkk and Aaaa. But implicit value should be defined in the scope, where you actually mixin Aaaa.

dk14
  • 22,206
  • 4
  • 51
  • 88
  • It's interesting that it still works via a derived class without its own implicit declaration. – richj Dec 26 '14 at 18:05
  • 1
    implicit applies at the moment of class definition, it's like `class Kkk extends Aaaa()(5)` – dk14 Dec 26 '14 at 19:49