简体   繁体   English

如何将外部作用域中的值隐式注入Scala特征

[英]How to implicitly inject a value from an outer scope into a Scala trait

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. 在示例中,我可以在'MicroServicesPrimaryStage'中使用'system',但不能在混合特性中使用'system'。 I think this is because 'system' is in scope but not considered to be defined as a member of 'MicroServicesPrimaryStage'. 我认为这是因为'system'在范围内,但不被认为是'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. 我可以使用val或def为'system'创建一个别名,并使其以这种方式工作,但这也意味着修改客户端代码的额外步骤。 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 编辑1

These two println statements illustrate the cause of my confusion: 这两个println语句说明了我混淆的原因:

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 编辑2

SBT file for use with Java 8: 用于Java 8的SBT文件:

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

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'." “在示例中,我可以使用'MicroServicesPrimaryStage'中的'system',但不能使用混合特性中的'system'。我认为这是因为'system'在范围内,但不被认为是'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. 如果运行,这将编译并打印“foo”。 Is it not what you are trying to do? 这不是你想要做的吗?

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.) (更新:夏洛特在技术上更正确;但在本季的精神中,水果蛋糕可能就是你所追求的。)

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 ) 让我们想象一下, Int是一个ActorSystem

This value will be accessible from both Kkk and Aaaa . 这个值可以从KkkAaaa But implicit value should be defined in the scope, where you actually mixin Aaaa . 但隐含的值应该在范围内定义,你实际上在混合Aaaa

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM