Skip to content

Instantly share code, notes, and snippets.

@p-pavel
Last active August 20, 2023 20:32
Show Gist options
  • Save p-pavel/964cab474f3f92ab5a00430253556626 to your computer and use it in GitHub Desktop.
Save p-pavel/964cab474f3f92ab5a00430253556626 to your computer and use it in GitHub Desktop.
Входы-выходы и логика
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()
}
}
@p-pavel
Copy link
Author

p-pavel commented Jun 16, 2019

Завершение работы прогни, когда счетчик дойдет до 100

@winitzki
Copy link

You may also find interesting to look at my new project https://github.com/winitzki/ui - currently at the stage of a very early proof-of-concept code.

@p-pavel
Copy link
Author

p-pavel commented Aug 20, 2023

starred, forked. Yo da man.

@p-pavel
Copy link
Author

p-pavel commented Aug 20, 2023

You may also find interesting to look at my new project https://github.com/winitzki/ui - currently at the stage of a very early proof-of-concept code.

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!

@winitzki
Copy link

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

@p-pavel
Copy link
Author

p-pavel commented Aug 20, 2023

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