Skip to content

Instantly share code, notes, and snippets.

@nartamonov
Created July 8, 2016 12:34
Show Gist options
  • Save nartamonov/f4071500da31b14cb3f66c7f390b5565 to your computer and use it in GitHub Desktop.
Save nartamonov/f4071500da31b14cb3f66c7f390b5565 to your computer and use it in GitHub Desktop.
Простейшая реализация FRP в виде сигналов, моделирующих значения, зависимые от времени.
/**
* Простейшая реализация FRP в виде сигналов, моделирующих значения, зависимые от времени.
*
* Из курса "Functional Program Design in Scala" (2016), Lecture 4.3
* https://www.coursera.org/learn/progfun2/lecture/5lWVa/lecture-4-3-a-simple-frp-implementation
*/
trait SimpleFRP {
class Signal[T](expr: => T) {
import Signal._
private var myExpr: () => T = _
private var myValue: T = _
private var observers: Set[Signal[_]] = Set()
update(expr)
def apply(): T = {
observers += caller.value
assert(!caller.value.observers.contains(this), "Cyclic signal definition")
myValue
}
protected def update(expr: => T): Unit = {
myExpr = () => expr
computeValue()
}
protected def computeValue(): Unit = {
val newValue = caller.withValue(this)(myExpr())
if (newValue != myValue) {
myValue = newValue
val obs = observers
observers = Set()
obs.foreach(_.computeValue())
}
}
}
private object NoSignal extends Signal[Nothing](???) {
override protected def computeValue(): Unit = {}
}
object Signal {
// StackableVariable не является thread-safe, поэтому конкурентный доступ
// к ней может вызывать race conditions. Вместо этого мы можем использовать
// scala.util.DynamicVariable с точно таким же интерфейсом, для того, чтобы
// состояние было локальным относительно потоков:
// private val caller = new scala.util.DynamicVariable[Signal[_]](NoSignal)
private val caller = new StackableVariable[Signal[_]](NoSignal)
def apply[T](expr: => T) = new Signal(expr)
}
class Var[T](expr: => T) extends Signal[T](expr) {
override def update(expr: => T): Unit = super.update(expr)
}
object Var {
def apply[T](expr: => T) = new Var(expr)
}
private class StackableVariable[T](init: T) {
private var values = List(init)
def value: T = values.head
def withValue[R](newValue: T)(op: => R): R = {
values = newValue :: values
try op finally values = values.tail
}
}
}
object Example extends App with SimpleFRP {
val a = Var(10)
val b = Signal { a() + 5 }
println(b()) // ==> 15
a() = 20
println(b()) // ==> 25
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment