Last active
January 7, 2016 03:03
-
-
Save kaisellgren/11393718 to your computer and use it in GitHub Desktop.
Academic Scala implementations of Functional Reactive Programming -- different Signal[A] and foldp[A, B] implementations as examples
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
// foldp[A, B] impl. for Signal[A], i.e. the STM -version. | |
// This function is defined as: foldp := (Signal<A>, (B, A) => B, B) => Signal<B> | |
def foldp[A, B](source: Signal[A], initial: B)(predicate: (B, A) => B): Signal[B] = { | |
val result = new Signal(initial) | |
source.listen({ ev: A => | |
result.set(predicate(result.get, ev)) | |
}) | |
result | |
} | |
object Foo extends App { | |
val clicks = new Signal(0) | |
val totalClicks = foldp(clicks, 0) { (previous: Int, current: Int) => previous + 1} | |
clicks.set(0) | |
clicks.set(0) | |
println(totalClicks.get) // 2! | |
} |
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
// How do I implement foldp[A, B] for SignalImmutable[A] (or SignalSM[A]) ? | |
// TODO: Does not work at all! | |
def foldpImmutable[A, B](source: SignalImmutable[A], initial: B)(predicate: (B, A) => B): SignalImmutable[B] = { | |
val result = SignalImmutable(initial) | |
SignalImmutable.listen({ ev: A => | |
// Great, so how do I actually update ´result´? | |
SignalImmutable.set(predicate(SignalImmutable.get(result), ev)) | |
})(source) | |
result | |
} | |
object Foo extends App { | |
val clicks = SignalImmutable(0) | |
val totalClicks = foldpImmutable(clicks, 0) {(previous: Int, current: Int) => previous + 1} | |
// This creates a new SignalImmutable, all hell breaks loose. | |
SignalImmutable.set(0)(clicks) | |
println(totalClicks.value) | |
} |
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
// This first Signal[A] impl. uses STM (Ref — transactional references). | |
class Signal[A](initial: A) { | |
type SignalListener = Function[A, Unit] | |
val value = Ref(initial) | |
val listeners = Ref(Seq[SignalListener]()) | |
def set(newValue: A): Unit = { | |
// Atomically change the value within a transaction, blocks the thread, but a not an issue since it's a simple operation. | |
// Finally return the current state/set of listeners and calls them. | |
atomic { implicit txn => | |
value() = newValue | |
listeners() | |
}.foreach(_(newValue)) | |
} | |
def get: A = atomic { implicit txn => | |
value() | |
} | |
/** Sets a new listener. */ | |
def listen(listener: SignalListener): Unit = atomic { implicit txn => | |
listeners() = listeners() :+ listener | |
} | |
} | |
object Foo extends App { | |
val slot = new Signal(0) | |
slot.listen({ value => | |
println(value) // Hooray! it prints 5! | |
}) | |
slot.set(5) | |
} |
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
// An immutable version called SignalImmutable[A]. Based on the concept of state transformations. | |
package object Foo { type SignalListener = Function[A, Unit] } | |
case class SignalImmutable[A](value: A, listeners: Seq[SignalListener] = Seq()) | |
object SignalImmutable { | |
def set[A](newValue: A)(signal: SignalImmutable[A]): SignalImmutable[A] = { | |
signal.listeners.foreach(_(newValue)) | |
signal.copy(value = newValue) | |
} | |
def get[A](signal: SignalImmutable[A]): A = signal.value | |
def listen[A](listener: SignalListener)(signal: SignalImmutable[A]): SignalImmutable[A] = { | |
signal.copy(listeners = signal.listeners :+ listener) | |
} | |
} | |
object Foo extends App { | |
// New instances after every state change. Ugly, yes. | |
val sig = SignalImmutable(1) | |
val sig2 = SignalImmutable.listen({v: Int => println(v)})(sig) | |
val sig3 = SignalImmutable.set(5)(sig2) | |
} |
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
// This version is immutable and uses state monads. | |
package object Foo { type SignalListener = Function[A, Unit] } | |
case class SignalSM[A](value: A, listeners: Seq[SignalListener] = Seq()) | |
object SignalSM { | |
def set[A](newValue: A): State[SignalSM[A], Unit] = State(signal => { | |
signal.listeners.foreach(_(newValue)) | |
(signal.copy(value = newValue), ()) | |
}) | |
def get[A]: State[SignalSM[A], A] = State(signal => (signal, signal.value)) | |
def listen[A](listener: SignalListener): State[SignalSM[A], Unit] = State(signal => { | |
(signal.copy(listeners = signal.listeners :+ listener), ()) | |
}) | |
} | |
object Foo extends App { | |
val state = for { | |
_ <- SignalSM.set(10) | |
_ <- SignalSM.listen({ value: Int => println(s"Here it is: $value")}) | |
_ <- SignalSM.set(20) | |
s <- State.get | |
} yield s | |
state.exec(SignalSM(5)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment