Last active
August 20, 2023 20:32
-
-
Save p-pavel/964cab474f3f92ab5a00430253556626 to your computer and use it in GitHub Desktop.
Входы-выходы и логика
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait IncrementDecrement[F[_]] { // Входы/выходы приложения инкремента-декремента | |
val increment: Stream[F,Unit] // Поток заявок на инкремент | |
val decrement: Stream[F,Unit] // Поток заявок на декремент | |
val total: Pipe[F,Int, INothing] // Переработка текущих значений в чистые побочные эффекты | |
} | |
object Logic { | |
import scala.concurrent.duration._ | |
// Как работать с IncrementDecrement | |
def run[F[_]:Concurrent: Timer](app: IncrementDecrement[F]): Stream[F,INothing] = { | |
val changes: Stream[F, Int] = app.increment.map(_ ⇒ 1) merge app.decrement.map(_ ⇒ -1) merge Stream(1).covary[F].repeat.metered(5.seconds) | |
val output : Stream[F, Int] = changes.scan(0)(_+_).takeWhile(_ < 100) | |
output through app.total | |
} | |
} | |
trait UIUtils { // Для удобства связи fs2 и JavaFX | |
/** Преобразуем события в поток */ | |
protected def attachHandler[F[_],A <: Event](setHandler: EventHandler[A] ⇒ Unit)(implicit F: ConcurrentEffect[F]): Stream[F,A] = | |
Stream.eval(Queue.unbounded[F,A]).flatMap{queue ⇒ | |
setHandler(ev ⇒ F.toIO(queue.enqueue1(ev)).unsafeRunAsyncAndForget()) | |
Stream.repeatEval(queue.dequeue1) | |
} | |
/** Преобразуем поток обновлений типа A в поток чистых эффектов | |
@todo chunk updates */ | |
protected def updatePipe[F[_],A](set: A ⇒ Unit)(implicit F:ConcurrentEffect[F]): Pipe[F,A,INothing] = | |
_.evalMap(a ⇒ F.delay(Platform.runLater(() ⇒ set(a)))).drain | |
def run(effects: Stream[IO,INothing]): Unit = effects.compile.drain.unsafeRunAsyncAndForget() | |
// Среда выполнения для IO | |
implicit val sched: ContextShift[IO] = IO.contextShift(ExecutionContext.global) | |
implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global) | |
} | |
class UI extends javafx.application.Application with UIUtils { | |
private val incButton = new Button("Increment") | |
private val decButton = new Button("Decrement") | |
private val label = new Label ("0") | |
private val hBox = new HBox(5, incButton, decButton, label) | |
def incDec[F[_]: ConcurrentEffect]: IncrementDecrement[F] = new IncrementDecrement[F] { | |
override val increment: Stream[F, Unit] = attachHandler(incButton.setOnAction).map(_ ⇒ ()) | |
override val decrement: Stream[F, Unit] = attachHandler(decButton.setOnAction).map(_ ⇒ ()) | |
val rotatePipe:Pipe[F,Int,INothing] = updatePipe(a ⇒ hBox.setRotate(a)) | |
val labelPipe: Pipe[F,Int,INothing] = _.map(_.toString).through(updatePipe(label.setText)) | |
override val total: Pipe[F, Int, INothing] = | |
_ | |
.broadcastThrough(rotatePipe, labelPipe) | |
.onFinalize( Sync[F].delay(System.exit(0))) | |
} | |
override def start(primaryStage: Stage): Unit = { | |
run(Logic.run(incDec[IO])) | |
val scene = new Scene(hBox) | |
primaryStage.setScene(scene) | |
primaryStage.show() | |
} | |
} |
The implementation of UI via streams is elegant from the theoretical point of view but not very practical. Compare the code in this gist
with the amount of code that implements essentially the same functionality (increment/decrement buttons) using the Elm architecture: ExampleSimpleElmProgram.scala
The implementation of UI via streams is elegant from the theoretical point of view but not very practical. Compare the code in this
gist
with the amount of code that implements essentially the same functionality (increment/decrement buttons) using the Elm architecture: ExampleSimpleElmProgram.scala
Again, I can't remember how this gist came to life :) Probably some proof of concept or something. Thanks for the link!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm about to hide my GitHub projects :) all of 'em were kind of discussions that lead mostly nowhere. I didn't expect that somebody will look at them :)
Thanks!