• Stars
    star
    490
  • Rank 89,811 (Top 2 %)
  • Language
    Haskell
  • Created about 10 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Comparing many FRP implementations by reimplementing the same toy app in each.

A window with 6 buttons labelled "0", "5", "10", "toggle", "toggle", and "toggle".

FRP Zoo

Interested in trying FRP (Functional Reactive Programming), but overwhelmed by the number of FRP libraries to choose from? To help you with this choice, this repository contains several implementations of the same small program, to give you a taste of what each library looks like.

library classification example app
artery scenarios 0 and 5, arrowized, signals code
auto scenarios 0 and 10, arrowized, signals code
DysFRP scenarios 0 and 10, higher-order, behaviours and events code
elerea scenarios 0 and 5, higher-order, signals code
euphoria scenario 0, higher-order, step signals, behaviours and events code
FRPNow scenario 0, higher-order, behaviours and events code
grapefruit scenario 0, higher-order, step signals, behaviours and events code
machinecell scenarios 0 and 5, arrowized, signals code
netwire all three scenarios, arrowized, continuous, signals code
varying all three scenarios, arrowized or applicative, continuous, signals code
ordrea scenario 0, higher-order, step signals, behaviours and events code
reactive-bacon scenarios 0 and 5, asynchronous data flow, behaviours and events code
reactive-banana scenario 0, higher-order, behaviours and events code
reflex scenarios 0 and 5, higher-order, behaviours and events code
Yampa scenarios 0 and 5, arrowized, continuous, signals code
sodium scenarios 0 and 5, higher-order, behaviours and events deprecated

For comparison, here are a few non-FRP implementations of the same small program.

library classification example app
โ€” callbacks code
โ€” pure functions code
objective scenario 0, push-pull automatons code

The TodoMVC of FRP libraries

If you haven't heard of TodoMVC, it's a website which hosts several implementation of the same simple Todo app, implemented using various Javascript frameworks and languages. This repository attempts to do the same thing, but with Haskell FRP libraries.

Instead of a Todo app, we use a small task which highlights the area in which FRP libraries differ the most: support for dynamic signal graphs.

Three scenarios

Evan Czaplicki gave an excellent presentation about the different categories of FRP libraries, in which he categorized FRP libraries according to the choices they make in their attempt to support dynamic graphs. He described the following example:

  1. Start with a graph which counts the number of clicks.
  2. Click 5 times.
  3. Change the graph so that it ignores the clicks.
  4. Click 5 more times.
  5. Change the graph back to the original graph.

Which number is displayed now? 0, because the new click counter hasn't received any click yet? 5, because the clicks did not reach the click counter while it was outside the graph? 10, because equational reasoning somehow dictates it?

The answer should not depend on the FRP library we choose, but on the behaviour we desire for our application! Thankfully, while each library gives a single answer for the above sequence of events, it is always possible to obtain the other outcomes using a different sequence of events. Our toy program will implement all three scenarios, but not necessarily by changing the underlying graph as described above.

Specification

A gloss window shall display six buttons, organized as three columns of two buttons. Each column implements one of the above scenarios: the first column chooses 0, the second column chooses 5, and the third column chooses 10. In each column, there is a button whose clicks are counted, and a toggle button to turn the click-counting on and off, starting with on. When off, the counter displays -1.

To be completely precise about the three scenarios:

  • The counter for scenario 0 displays the number of clicks since the launch of the app, or since the corresponding toggle button was last toggled on, whichever is most recent.
  • The counter for scenario 5 displays the total number of clicks which were received while the corresponding toggle was on.
  • The counter for scenario 10 displays the total number of clicks since the launch of the app.

Static implementation

In order to compare equivalent features in each library, the first half of the implementation should implement the required behaviour using only the core FRP features which are common to all FRP libraries: filtering events, combining current values, and accumulating changes to a local state.

To be completely precise about the expected static graph:

  • Each toggle button accumulates not operations on a boolean, to establish whether the mode is currently on or off.
  • This boolean is used to filter the click events which reach the counter for scenario 5.
  • The counters for scenarios 5 and 10 accumulate (+1) operations on an integer.
  • The counter for scenario 0 accumulates const 0 and (+1) operations, depending on whether the incoming event is a toggle or a click.
  • The current counters and modes are combined so that -1 is used when the mode is off.

Dynamic implementation

In order to compare the parts of the libraries which differ from each other, this second half of the implementation should reimplement some of the scenarios from part one using dynamic graph modifications, when appropriate. Libraries which only support static graphs should skip this section (I hasten to point out that focusing on static graphs is a perfectly valid point in the design space, as explained in Evan's presentation).

For libraries which do support graph modifications, it is likely that only one of the scenarios can be implemented: temporarily remove the counter from the graph while the toggle is off, then try out the application to see which scenario occurs. In the unlikely case of a library which supports more than one way to modify its graph, more than one scenario might be implementable.

Due to the variability, I cannot give a more precise description of the task, but I do want to point out a common trap: don't reimplement first-order primitives using the higher-order ones. In particular:

  • For scenario 5, it is tempting to switch between a graph which generates click events and a graph which doesn't, but this is simply a filter reimplementation. Instead, demonstrate that the events don't reach the counter when it is outside the graph, by switching between two counters for example.
  • For scenario 10, it is tempting to separately define a counter which counts all clicks and then to switch between this counter and -1, but this is simply combining the current values of existing signals. Instead, demonstrate that events are recorded and replayed, by hiding the definition of your counter inside a conditional for example.

Gloss integration

Since the goal is to compare FRP implementations, not GUI systems, a simple implementation of the 6 buttons GUI is provided, along with methods to determine whether the current event is a click or a toggle. Gloss supports both IO-based and state-based APIs, which should make it easy to hook up any FRP library. See the existing implementations for details.

Classification

Evan's presentation classifies FRP libraries into four categories according to the choices they make regarding dynamic graphs. In our list of implementations at the top of this page, we tag each library with the category it belongs to, as well as the scenarios it can implement via dynamic graph changes. There are also other important distinctions between libraries which have nothing to do with dynamic graphs, whose corresponding tags are described in this section.

  • First-order FRP: from Evan's classification, an FRP library which only supports static graphs.
  • High-order FRP: from Evan's classification, an FRP library in which event streams are infinite and the graphs can be changed by collapsing a signal of signals of values into a signal of values.
  • Asynchronous data flow: from Evan's classification, an FRP library in which fast event-processing nodes may receive more recent events than their slower neighbours. Some versions of this model support "cold" signals, in which the event processing is skipped if nobody is listening for the results.
  • Arrowized FRP: from Evan's classification, an FRP library in which graph nodes are automatons which may or may not tick each frame, depending on whether or not they are currently part of the graph. Best for scenario 5. Another way to view this category is that the primary abstraction isn't signals, but functions between signals.
  • Events and behaviours: an FRP library in which there are two kinds of reactive objects: behaviours hold a value at every point in time, while events only hold values when the event they represent occurs.
  • Signals: an FRP library in which all reactive values hold a value at every point in time. Typically, events are represented via Maybe.
  • Step signals: a separate representation for signals whose value only changes at specific points in time, typically when an event occurs.
  • Continuous: an FRP library in the style of Conal Elliott, meaning that signals are functions from time to values. This built-in notion of time allows interpolation between values, and other time-based transformations.

Contributing

If the example app for your favourite FRP library is missing or non-idiomatic, or if there are other axes of comparison which you think should also be considered, feel free to open an issue or to send a pull request!

More Repositories

1

hawk

Haskell text processor for the command-line
Haskell
361
star
2

git-slides

Text-based slides using vim and git.
Shell
152
star
3

klister

an implementation of stuck macros
Haskell
129
star
4

category-syntax

do-notation for Category and "Arrow without arr"
Haskell
63
star
5

typelevel-rewrite-rules

rewrite rules for type-level equalities
Haskell
62
star
6

linear-examples

Example uses of linear types
Haskell
42
star
7

n-ary-functor

A single typeclass for Functor, Bifunctor, Trifunctor, etc.
Haskell
40
star
8

conway

Demonstrating comonad transformers.
Haskell
35
star
9

hyzzy

A framework for defining text adventures via Haskell files. Play by combining functions, not by guessing phrases.
Haskell
32
star
10

ludum-dare-31

The theme for LD31 was "Entire Game on One Screen"
Haskell
29
star
11

magic-typelevel-elem

Demonstrating how to make type families faster using typechecker plugins
Haskell
21
star
12

cabal-rangefinder

A tool to fill in the version ranges in a cabal file.
Haskell
17
star
13

mastering-haskell

The slides for my Packt course, "Mastering Haskell".
Shell
15
star
14

commutative

Using Haskell's type system to guarantee commutativity.
Haskell
15
star
15

typechecker-combinators

Haskell
15
star
16

laughable

Clowns to the left of me, jokers to the right
Haskell
13
star
17

deploy-hint

Demonstrating that you don't need to install ghc in order to use the hint library.
Haskell
11
star
18

surjective

An output coverage checker
Haskell
11
star
19

giggles-is-you

A reimplementation of Baba is You in Haskell, for our weekly haskell-beginners presentations.
Haskell
9
star
20

strongly-typed-bound

My version of Kmett's "strongly typed bound for acowley" snippet.
Haskell
9
star
21

circular-sig

a twelf-like type-checker supporting recursive signatures
Haskell
8
star
22

objc2java

Convert pure ObjectiveC code to pure Java code, libraries be damned.
Haskell
8
star
23

ludum-dare-35

The theme for LD35 was: "shapeshift"
Elm
8
star
24

nominalize

Generate types using type-generic programming, retaining control over the names of the constructors and the fields.
Haskell
6
star
25

image-watcher

Display an image and update it when the file changes
Haskell
6
star
26

ludum-dare-34

The themes for LD34 were: "two button controls" and "growing"
JavaScript
6
star
27

apecs-hint-demo

demonstrating how to use hint to dynamically modify the game world of an apecs-based game
Haskell
5
star
28

slides

A place to host my git-slides presentations
5
star
29

stm-variants

The STM API we know and love, but useable in more circumstances
Haskell
5
star
30

acme-tiny-rules

a parody of Tiny Glade with inference rules instead of castles
Haskell
5
star
31

dot-utils

A set of small command-line tools for transforming GraphViz dot-files
Haskell
4
star
32

k-playground

for implementing toy languages using the K Framework
Haskell
4
star
33

acme-circular-containers

Spineless containers which are fast to read but inefficient to update
Haskell
4
star
34

jira-dependencies

from JIRA's .csv export to graphviz's dot format
Haskell
4
star
35

tarjan

an agda implementation of Tarjan's strongly connected components algorithm
Agda
3
star
36

chopt-test-task

Haskell
3
star
37

worldly

based on Conor McBride's "Worldly Type Systems" talk
Haskell
3
star
38

ludum-dare-44

The theme for LD44 was "Your life is currency"
Rust
3
star
39

agda-playground

a series of ambitious experiments in the functional language / proof assistant Agda.
Vim Script
3
star
40

transliterator

Haskell
3
star
41

premonoidal

Agda encoding of premonoidal categories
Agda
3
star
42

submake

Use git to replay part of a bash pipeline.
Haskell
2
star
43

directed-graph-ui

A tool for creating directed graphs.
JavaScript
2
star
44

ice-cream-privacy

Haskell
2
star
45

rerec

Haskell
2
star
46

hs-promote

C++-style type promotion for Haskell's numeric hierarchy
Haskell
2
star
47

hint-demo

Haskell
2
star
48

trie-based-frp

An implementation of higher-order FRP based on shared tries instead of unsafePerformIO.
Haskell
2
star
49

glut-events

For writing GLUT's main loop in the style of Haskell's main function.
Haskell
2
star
50

reactive-banana-anti-tutorial

companion code for my blog post
Haskell
2
star
51

queues

Compare the performance of a few Haskell queue implementations.
Haskell
2
star
52

relocation-bug

Haskell
1
star
53

raml

C
1
star
54

contravariant-case

An API for Divisible and Decidable which looks like pattern-matching.
Haskell
1
star
55

ludum-dare-42

The theme for LD42 was "Running out of Space"
TypeScript
1
star
56

adicity

A DSL in which composition and application are unified.
Haskell
1
star
57

stack-bug

A demonstration of a bug which doesn't fit in a gist because it requires directories.
Haskell
1
star
58

free-premonoidal

Haskell
1
star
59

existentials

A type-level DSL for placing existential constraints on some arguments of a type constructor
Haskell
1
star
60

haskell-code-explorer-bug

a minimal repro
Haskell
1
star
61

croportunity-cost

A game about clicking and not clicking on plants.
Elm
1
star
62

ludum-dare-43

Haskell
1
star
63

timesheet

Simple text-based time tracking
Haskell
1
star
64

pure-framework-chat

Haskell
1
star
65

tic-tac-top

The AI for a tiny board game I invented.
Haskell
1
star
66

face-down

Haskell
1
star
67

install-before-test

How to configure project.cabal so that tests are run after the install.
Haskell
1
star
68

memento

Write a Virtual DOM for anything!
Haskell
1
star
69

dotfiles

My default setup, including config files and shell scripts.
Python
1
star
70

gif-browser

a tiny application for browsing through the frames of an animated gif.
Haskell
1
star
71

lens-syntax

Pointful syntax for optic compositions
Haskell
1
star