首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在fs2功能流中为Scala创建离散流?

如何在fs2功能流中为Scala创建离散流?
EN

Stack Overflow用户
提问于 2017-08-04 17:08:57
回答 1查看 560关注 0票数 3

是否可以在fs2中创建离散事件流?如果是的话该怎么做。我刚开始和图书馆玩,我知道我有很多东西要学。但我没有看到任何与此相关的例子。例如,我希望在标尺或swing中为"mousemove“或"click”创建一个流。我正在寻找类似于在Rx.Observable.create中使用RxJS来创建离散事件的东西,比如:

代码语言:javascript
复制
//note: pseudo code
var mouse = Rx.Observable.create( subscriber => {
     document.body.addEventListener("mousemove", event =>{
      subscriber.onNext(event)
 })
} ) 

fs2中的等价物可能并不是那么琐碎,但如果有人能建议我如何做。我想它将使用Handler和拉/推数据类型,但我很难理解如何使用。

干杯。

EN

回答 1

Stack Overflow用户

发布于 2019-02-05 05:23:56

下面是我提出的一个示例,演示了如何在fs2中使用JavaFX:

代码语言:javascript
复制
import cats.implicits._
import cats.effect._
import cats.effect.implicits._
import javafx.application.{Application, Platform}
import javafx.scene.{Node, Scene}
import javafx.scene.layout._
import javafx.stage.Stage
import fs2._
import fs2.concurrent._
import javafx.beans.value.WritableValue
import javafx.scene.control.{Label, TextField}
import javafx.scene.input.KeyEvent

import scala.concurrent.ExecutionContext

import scala.util.Try

class Fs2Ui extends Application {
  override def start(primaryStage: Stage): Unit = {
    implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
    implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global)

    new Logic[IO]().run(primaryStage).start.unsafeRunSync()
  }

  class Logic[F[_]: ConcurrentEffect: ContextShift: Timer] {
    import Fs2Ui._
    import java.time.{Duration, Instant}
    import java.util.concurrent.TimeUnit.MILLISECONDS

    def run(primaryStage: Stage): F[Unit] = for {
      v <- initializeUi(primaryStage)
      View(input, feedback) = v

      _ <- Stream(input).covary[F]
        .through(typedChars)
        .through(processInput)
        .through(displayFeedback(feedback.textProperty))
        .compile.drain
    } yield ()

    private def initializeUi(primaryStage: Stage): F[View] = updateUi {
      val input = new TextField()
      input.setPrefWidth(300)
      val feedback = new Label("...")

      val vbox = new VBox(input, feedback)
      val root = new StackPane(vbox)
      val scene = new Scene(root)

      primaryStage.setScene(scene)
      primaryStage.show()

      View(input, feedback)
    }

    private def processInput: Pipe[F, TypedChar, Feedback] = for {
      typed <- _
      _ <- Stream.eval(ContextShift[F].shift)
      res <- Stream.eval { time(processSingle(typed)) }
      (d, Feedback(str)) = res
    } yield Feedback(s"$str in [$d]")

    private def displayFeedback(value: WritableValue[String]): Pipe[F, Feedback, Unit] =
      _.map { case Feedback(str) => str } through updateValue(value)

    private def time[A](f: F[A]): F[(Duration, A)] = {
      val now = Timer[F].clock.monotonic(MILLISECONDS).map(Instant.ofEpochMilli)
      for {
        start <- now
        a <- f
        stop <- now
        d = Duration.between(start, stop)
      } yield (d, a)
    }

    private val processSingle: TypedChar => F[Feedback] = {
      import scala.util.Random
      import scala.concurrent.duration._

      val prng = new Random()
      def randomDelay: F[Unit] = Timer[F].sleep { (250 + prng.nextInt(750)).millis }

      c => randomDelay *> Sync[F].delay(Feedback(s"processed $c"))
    }
  }
}

object Fs2Ui {
  case class View(input: TextField, feedback: Label)

  case class TypedChar(value: String)
  case class Feedback(value: String)

  private def typedChars[F[_]: ConcurrentEffect]: Pipe[F, Node, TypedChar] = for {
    node <- _
    q <- Stream.eval(Queue.unbounded[F, KeyEvent])
    _ <- Stream.eval(Sync[F].delay {
      node.setOnKeyTyped { evt => (q enqueue1 evt).toIO.unsafeRunSync() }
    })
    keyEvent <- q.dequeue
  } yield TypedChar(keyEvent.getCharacter)

  private def updateValue[F[_]: Async, A](value: WritableValue[A]): Pipe[F, A, Unit] = for {
    a <- _
    _ <- Stream.eval(updateUi(value setValue a))
  } yield ()

  private def updateUi[F[_]: Async, A](action: => A): F[A] =
    Async[F].async[A] { cb =>
      Platform.runLater { () =>
        cb(Try(action).toEither)
      }
    }
}

演示fs2和JavaFX之间绑定的具体部分是两个Pipes:typedCharsupdateValue。就我个人而言,我认为最具挑战性的部分是让KeyEvent侦听器看起来像事件的fs2 Stream

代码语言:javascript
复制
node.setOnKeyTyped { evt => (q enqueue1 evt).toIO.unsafeRunSync() }
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45512201

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档