diff --git a/README.md b/README.md index 525c439..6f7291c 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,20 @@ [![Gitter](https://badges.gitter.im/scalaz/scalaz-reactive.svg)](https://gitter.im/scalaz/scalaz-reactive?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -# Goal +## Goal A high-performance, purely-functional library for reactive programming based on efficient incremental computation. -# Introduction +## Introduction This library aims at faithfully implementing Functional Reactive Programming as defined in [2]. The term _Reactive programming_ is often used to describe composing streams of discrete events. Functional reactive programming (FRP) is about composing dynamic values changing in continuous time and reacting to discrete events. -# Core concepts +## Core concepts -`Behaviour[A](value: Reactive[TimeFun[A]])` - value chaninging over time. +`Behaviour[A](value: Reactive[TimeFun[A]])` - value changing over time. `Event[+A](value: Future[Reactive[A]])` - stream of (Time, a) pairs. @@ -24,12 +23,11 @@ composing dynamic values changing in continuous time and reacting to discrete ev `Sink[A, B](f: A => IO[Void, Unit])` - consumer of reactive values. - -# Example +## Example This project is just starting, so the working example is quite simple: -``` +```scala case class Tick(name: String) def ticks(interval: Duration, name: String): Event[Tick] = @@ -45,7 +43,7 @@ case class Tick(name: String) This program produces a `scalaz.zio.IO` that can be run by e.g. `scalaz.zio.App` - see `TwoTickers.scala` in `examples`. -# Background +## Background * _Functional Reactive Programming_ by Stephen Blackheath and Anthony Jones, Manning Publications * Push-Pull Functional Reactive Programming [paper](http://conal.net/papers/push-pull-frp/) by Conal Elliot diff --git a/core/src/main/scala/scalaz/reactive/Behaviour.scala b/core/src/main/scala/scalaz/reactive/Behaviour.scala index a7d0c9f..9a1b00d 100644 --- a/core/src/main/scala/scalaz/reactive/Behaviour.scala +++ b/core/src/main/scala/scalaz/reactive/Behaviour.scala @@ -2,6 +2,11 @@ package scalaz.reactive import scalaz.{ Applicative, Functor } +/** + * Semantically, a behaviour is simply a function of time. A reactive behaviour is composed + * of a discrete part, represented as a reactive value, `Reactive`, and a continuous + * part, represented as a time function,`TimeFun`. + */ case class Behaviour[A](value: Reactive[TimeFun[A]]) extends AnyVal { def map[B](f: A => B): Behaviour[B] = diff --git a/core/src/main/scala/scalaz/reactive/Event.scala b/core/src/main/scala/scalaz/reactive/Event.scala index 597f241..927bb26 100644 --- a/core/src/main/scala/scalaz/reactive/Event.scala +++ b/core/src/main/scala/scalaz/reactive/Event.scala @@ -5,6 +5,23 @@ import scalaz.zio.{ Fiber, IO } import scala.concurrent.duration.Duration +/** + * Semantically, an Event is time-ordered lists of future values, where a future value + * is a time/value pair: [(t0 , a0 ), (t1 , a1 ), ...]. If such an occurrence list is + * nonempty, another view on it is as a time t0, together with a reactive value having + * initial value a0 and event with occurrences [(t1,a1),...]. If the occurrence list is + * empty, then we could consider it to have initial time ∞ (maxBound), and reactive value + * of ⊥. Since a future value is a time and value, it follows that an event (empty or nonempty) + * has the same content as a future reactive value. This insight leads to a new representation + * of functional events: + * + * {{{ + * -- for non-decreasing times + * newtype Event a = Ev (Future (Reactive a)) + * }}} + * + * See more details in section 6 at http://conal.net/papers/push-pull-frp + */ case class Event[+A](value: Future[Reactive[A]]) { self => def delay(interval: Duration): Event[A] = Event(value.delay(interval)) diff --git a/core/src/main/scala/scalaz/reactive/Future.scala b/core/src/main/scala/scalaz/reactive/Future.scala index 08299b6..18d60ee 100644 --- a/core/src/main/scala/scalaz/reactive/Future.scala +++ b/core/src/main/scala/scalaz/reactive/Future.scala @@ -3,6 +3,9 @@ package scalaz.reactive import scalaz.Scalaz._ import scalaz.zio.IO +/** + * A value with an associated time, `(t,a)` + */ object Future { type Future[+A] = IO[Void, (Time, A)] diff --git a/core/src/main/scala/scalaz/reactive/Reactive.scala b/core/src/main/scala/scalaz/reactive/Reactive.scala index 94ae806..285cc7c 100644 --- a/core/src/main/scala/scalaz/reactive/Reactive.scala +++ b/core/src/main/scala/scalaz/reactive/Reactive.scala @@ -3,6 +3,12 @@ package scalaz.reactive import scalaz.{ Applicative, Functor, Monad } import Future._ +/** + * A reactive value is like a reactive behaviour but is restricted to changing discretely. Its + * meaning is a step function defined by an initial value, `head`, and discrete changes, `tail`. + * Each discrete change is defined by a time and a value, which correspond exactly to an FRP `Event`. + * (See section 5.2 of Elliott's Push-Pull FRP paper) + */ case class Reactive[+A](head: A, tail: Event[A]) { def map[B](f: A => B): Reactive[B] = diff --git a/core/src/main/scala/scalaz/reactive/Sink.scala b/core/src/main/scala/scalaz/reactive/Sink.scala index f09f13f..3d54b98 100644 --- a/core/src/main/scala/scalaz/reactive/Sink.scala +++ b/core/src/main/scala/scalaz/reactive/Sink.scala @@ -1,6 +1,9 @@ package scalaz.reactive import scalaz.zio.IO +/** + * A consumer for the generated values. + */ object Sink { type Sink[A] = A => IO[Void, Unit] diff --git a/core/src/main/scala/scalaz/reactive/TimeFun.scala b/core/src/main/scala/scalaz/reactive/TimeFun.scala index f7ec0a4..8337ebd 100644 --- a/core/src/main/scala/scalaz/reactive/TimeFun.scala +++ b/core/src/main/scala/scalaz/reactive/TimeFun.scala @@ -4,6 +4,9 @@ import scalaz.reactive.Time._ import scalaz.{ Applicative, Functor, Monad } import scalaz.reactive.TimeFun.{ Fun, K } +/** + * A function of time. It has functor, applicative, and monad instances + */ sealed trait TimeFun[+A] { def apply: Time.T => A diff --git a/examples/src/main/scala/scalaz/reactive/examples/awt/AwtKeyAndMouse.scala b/examples/src/main/scala/scalaz/reactive/examples/awt/AwtKeyAndMouse.scala index 4cf4fa5..d9415fc 100644 --- a/examples/src/main/scala/scalaz/reactive/examples/awt/AwtKeyAndMouse.scala +++ b/examples/src/main/scala/scalaz/reactive/examples/awt/AwtKeyAndMouse.scala @@ -4,7 +4,7 @@ import java.awt.event._ import java.awt.{ BorderLayout, Dimension } import java.util.EventObject -import javax.swing.{ JButton, JScrollPane, JTextArea, _ } +import javax.swing.{ JButton, JScrollPane, JTextArea, WindowConstants } import scalaz.reactive.Event import scalaz.reactive.Sink.Sink import scalaz.zio.{ IO, _ } @@ -20,7 +20,7 @@ object KeyboardAndTimeApp extends App with RTS { class KeyboardAndTimeApp(name: String) extends AwtApp(name) { - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE) val displayArea = new JTextArea buildPane()