【问题标题】:How to implicitly inject a value from an outer scope into a Scala trait如何将外部范围的值隐式注入 Scala 特征
【发布时间】:2014-12-25 15:10:01
【问题描述】:

我正在尝试定义一个期望值在外部范围内的可重用特征。我可以在外部范围内定义特征,它可以工作,但不能重用。当我将特征移动到单独的范围时,特征无法访问该值,并且我还没有找到一种方法来将其声明为存在于特征混合到的类型的外部范围中。

到目前为止,我得到的最接近的是:

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

它非常接近我所追求的,唯一的好处是客户端代码需要将外部范围内的值声明为隐式。理想情况下,我希望客户端代码在不更改任何其他内容的情况下混合特征。

在示例中,我可以在“MicroServicesPrimaryStage”中使用“system”,但不能在混合特征中使用。我认为这是因为“系统”在范围内,但不被视为被定义为“MicroServicesPrimaryStage”的成员。

我可以使用 val 或 def 为“system”创建别名并使其以这种方式工作,但这也意味着修改客户端代码的额外步骤。如果 trait 可能需要定义 'system' 并且 能够在混入 trait 的外部范围内找到它,那就太好了。

这可能吗?

编辑 1

这两个 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
  ...

我不认为蛋糕模式可以自己解决这个问题,因为问题在于类型系统如何与外部范围内的定义进行交互。

编辑 2

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

【问题讨论】:

  • 不确定这是否有帮助,但是您可以声明对可以声明特征的位置的约束,例如:trait Tax { this: Trade =>(来自:debasishg.blogspot.hu/2010/02/…
  • 感谢您的建议。我尝试了自类型并选择直接扩展“微服务”而不是限制最终类型这样做。这可能是一个更好的起点,但我认为这两种方法都会导致编译器需要在最终类型(“MicroServicesPrimaryStage”)中明确定义“actorSystem”。我认为有趣的是,外部对象的成员不被视为在外部对象范围内混合在一起的类的成员。
  • 我找到了解释差异的答案,也许自我类型更清楚地表达了意图:stackoverflow.com/questions/7250374/…
  • 自包含代码很有帮助(或者是 build.sbt,因为我很懒),更新问题以反映 cmets 中的喋喋不休很有帮助。

标签: scala implicit traits self-type


【解决方案1】:

你错了:

"在示例中,我可以在 'MicroServicesPrimaryStage' 中使用 'system',但不能在混合特征中使用。我认为这是因为 'system' 在范围内但不被视为定义为'MicroServicesPrimaryStage' 的成员。”

这不是真的。您当然可以使用超类成员作为混合特征的抽象成员的定义。考虑一下:

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
}

这会编译并在运行时打印“foo”。这不是你想要做的吗?

【讨论】:

  • 这是另一种情况。我试图使用来自外部范围的值作为定义而不重新定义它。如果您将“val foo”定义从“class Bar”移到“object FooBar”中,您的示例将更接近我想要实现的目标 - 但这不会编译,这就是我不得不使用的原因隐含的。
  • 啊,好吧,我明白了,你在说什么。好吧,如果您的问题只是使用隐式,则不必如此。为什么不简单地将system 作为参数传递给类构造函数呢?我更新了我的答案来说明这一点。
  • 我实际上按照你说的做,但是(几乎)使用隐式从客户端代码中隐藏参数的存在。 Scala 让我能走到这一步真是太神奇了,但我仍在寻找一种“更清洁”的方式来从客户端代码的角度实现相同的结果。
  • 是的,你确实在“隐藏”它......我只是不确定这是一件好事。你喜欢魔法吗?
【解决方案2】:

也许这就是你要找的东西:

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

让我们想象一下,Int 是一个ActorSystem)

可以从KkkAaaa 访问此值。但是隐式值应该在你实际混入Aaaa的范围内定义。

【讨论】:

  • 有趣的是,它仍然可以通过没有自己隐式声明的派生类工作。
  • 隐式应用在类定义的那一刻,就像class Kkk extends Aaaa()(5)
【解决方案3】:

对不起,如果我也遗漏了什么。

这只是经典的夏洛特蛋糕图案。

或者,也许您要的是水果蛋糕,下一层会有额外的惊喜。 (也许国王的蛋糕是一个更好的比喻。)

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

我不知道为什么它应该是黄色的,除了在这种情况下黄色比磅更有趣。 (更新:夏洛特在技术上更正确;但本着这个季节的精神,水果蛋糕可能是你所追求的。)

【讨论】:

  • 我以为你在做某事,但是将继承转移到外部对象会产生不同的问题。我开始研究这个问题,因为我发现 ScalaFX 窗口的默认关闭行为有点原始,我认为这很容易通过对客户端代码进行一行更改来解决,后面有一点可重用的“库”代码场景。隐含的方法还可以,但我认为必须有更好的方法。自类型是 trait 继承的替代方案,但在客户端代码的 [重] 可用性方面,这两种方法是等效的。
猜你喜欢
  • 1970-01-01
  • 2016-05-28
  • 2015-02-19
  • 2023-03-19
  • 1970-01-01
  • 2018-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多