• Stars
    star
    132
  • Rank 267,740 (Top 6 %)
  • Language
    Haskell
  • License
    BSD 3-Clause "New...
  • Created almost 5 years ago
  • Updated about 1 year ago

Reviews

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

Repository Details

A remote virtual DOM library for Haskell

Replica

CircleCI

Remote logo

Replica is a remote virtual DOM library for Haskell. In contrast to traditional virtual DOM implementations, a remote virtual DOM runs on the server and is replicated to the client, which just acts as a dumb terminal.

Remote DOM

Quick start

To see it in action, check out the list of Replica frameworks

Motivation

SPAs written in frameworks such as React or Vue often require setting up projects in different languages with their respective build tooling; thinking about protocols between server and client; designing data types for communication (whether explicitly or implicitly) and coming up with a way to keep everything in sync reliably. More often than not this results in copious amounts of boilerplate and support code completely unrelated to the task at hand.

A change on the backend has to be propagated through the server code and protocol or API long before the affected UI components can be adjusted. Conversely, changes in the UI specification or requirements depend upon long-winded thought experiments in data flow before the required modifications on the backend can be pinned down. Prototyping speed slows down and maintenance costs go up.

And so, one often finds oneself longing for the Olden Days of server-side HTML rendering, where all the data was readily available at our fingertips without protocols or different ecosystems to deal with. However, this comes at the cost of losing out on designing UIs declaratively in the spirit of Elm or React and of course on the interactivity displayed by code running directly on the client side.

Remote virtual DOM

Replica seeks the middle ground โ€“ it runs a virtual DOM remotely on the server while pushing only minimal diffs to the client, which in turn sends events triggered by the user back to the backend. This offers all the advantages of generating a complete UI on the server, while still providing the interactivity of more traditional SPAs running entirely on the client.

A word about data volume โ€“ currently, the data format is not optimised for size at all; that said, it should be comparable to a finely hand-tuned protocol, since only the most minimal changeset is sent over the wire (save for a small-ish constant overhead because of the ceremony caused by the recursive tree nature of the data). In contrast, many APIs are constructed to send the whole requested dataset on every change, so Replica's diffs might fare favourably here. To that point, designing a protocol with minimal data volumes in mind is however also very likely to increase the volume of the aforementioned incidental boilerplate and support code.

Preventing common errors

There is also the additional burden of thinking about sensitive data โ€“ a simple select * from users would also send the (hopefully) hashed user passwords back to the client, even though they are not displayed anywhere in the UI. Thinking hard about scrubbing records before throwing something over the wire is laborious and error prone, and humans make mistakes.

With a remote VDOM, all code runs on the backend โ€“ no need to come up with ways to share functionality between server and client, to validate forms twice or authenticate API requests.

That is not to say that Replica itself (or its programming model) is more secure than any other program exposed to a network, but a certain class of common errors stemming from the split between back- and frontend are presumably more difficult to make.

Bundle sizes

Megabyte-sized code bundles including hundreds of dependencies can certainly take a bit to download, however parsing and compiling JavaScript is often just as slow. With a remote VDOM, no code at all is shipped to the client (except for the JS driver), only diffs and assets like CSS, images, or fonts. Although there's no support for server-side HTML rendering yet, once implemented it will be just as easy to push everything at once (without having to wait for the driver to establish a WebSocket connection and request an initial DOM update), reducing the time to first render even further.

Testing

Since there is no dependency on a browser environment, simulating UI interactions is just a matter of getting hold of the VDOM and firing a synthesized event on a particular node. QuickChecking components or even metamorphic testing are certainly some interesting avenues to explore in the context of UI testing.

Client side prediction

Replica runs over a WebSocket connection. A virtual DOM wired up with events which might lead to change needs to react to every such event โ€“ be it a mouse click or a key stroke. Normally, even on an average connection, some lag between a click on a button and showing the updated UI is fine; not so when it comes to typing. Even a lag of ~50ms becomes noticeable, since the virtual DOM is a complete and accurate representation of the UI displayed to the user and the values of text input elements need to be replicated as well.

Game developers have had to deal with the problem of client side prediction at least since the days of Quake and so the solution space is well understood. For the time being, Replica's algorithm is simple โ€“ every event โ†’ DOM patch roundtrip increases a frame number on the server and every such patch is tagged with said frame number. Additionally, every input element wired with an event listener keeps its value in a capped queue in the browser DOM for a given number of frames (currently 20). Finally, when the DOM is patched, the input value is not touched if the server value matches any of the previous frame values stored on the client.

Even with a simple scheme like this, the user experience is indistinguishable from code running directly on the client, for the majority of cases, for even higher lag values of ~100โ€“200ms. Rare edge cases do show, but those can be mitigated against in the future by employing more sophisticated client-side prediction algorithms.

Caveats

Since DOM diffing runs on the server, Replica is more resource intensive than a comparable backend implementation which just returns data without diffing. It's not recommended to use it for high-traffic user-facing applications. It might however be the perfect fit for internal tooling where in many cases prototyping and maintenance costs trump hardware prices by a wide margin.

Additionally, events such as onMouseMove are discouraged, even though they are supported in principle. This is because there might be better ways to provide high interactivity in the future โ€“ for example, by implemeting a custom, highly optimised onMouseDrag event โ€“ than bombarding the server with a torrent of movement events.

There's no support for animations and lifecycle events yet, but the implementation would be relatively straightforward.

Building

Install TypeScript and Stack. Then:

cd js && tsc --project tsconfig.json && cd ..
stack build --test

The TypeScript step must be executed whenever js/client.ts changes.

Integration with UI frameworks

Replica aims to be framework-agnostic. It has a simple API and hooking into it should be as uncomplicated as possible.

The easiest way to run Replica is to call Network.Wai.Handler.Replica.app. This takes care of distributing a complete index.html together with the JavaScript driver to the client. Everything should work out of the box.

For finer grained integration, there's Network.Wai.Handler.Replica.websocketApp, which just handles the WebSocket part of the connection; the root js/index.html and js/dist/client.js must be distributed separately.

Replica.VDOM.diff and Replica.VDOM.fireEvent allow for complete control over the remote virtual DOM.

List of Replica frameworks

Roadmap

  • Hackage documentation
  • Initial server-side rendering
  • SVG support (@seagreen)
  • Better diffing algorithm
  • Lifecycle events and animation hooks
  • onMouseDrag event
  • Nix derivation
  • QuickCheck component framework

Bugs and features

Comments, bug reports and feature PRs very much welcome.

More Repositories

1

atea

A minimalistic menu bar time tracker for MacOS
Clojure
613
star
2

leerraum.js

A PDF typesetting library with exact positioning and hyphenated line breaking
TypeScript
231
star
3

concur-replica

Server-side VDOM UI framework for Concur
Haskell
138
star
4

typedraw

Visually describe Haskell/Purescript/Elm types
JavaScript
105
star
5

concur-static

Generate semi-dynamic UIs with Concur
Haskell
58
star
6

purescript-refract

Optical Purescript UI library based on React and the Elm architecture, but without the boilerplate.
PureScript
48
star
7

knit

Ties the knot on data structures that reference each other by unique keys
Haskell
46
star
8

sherlock

Structured json log viewer in the browser
JavaScript
20
star
9

synchron

Synchronous programming in Haskell
Haskell
15
star
10

formulae

Mirror of ftp://ftp.cs.uni-sb.de/formulae - "A Functional Description of TeX's Formula Layout"
Standard ML
12
star
11

atea-contrib

User contributed content for Atea
8
star
12

ytools

A set of structured composable unix tools
Haskell
8
star
13

purescript-interop

Generate Purescript types from Haskell
Haskell
7
star
14

memdb

Efficient in memory indexed database
Haskell
6
star
15

fbookmark

A file based bookmark plugin for Pentadactyl
JavaScript
6
star
16

usercfg

usercfg โ€” remote account configuration protocol
Haskell
5
star
17

purescript-trace

Easily trace function arguments in Purescript.
JavaScript
4
star
18

bookkeeper-permissions

Permissions for bookkeeper records
Haskell
4
star
19

quadtree

Mutable and immutable fast quadtrees
Haskell
3
star
20

refract-replica

Refract backend for Replica
Haskell
3
star
21

purescript-thermite-login-component

Login component for purescript-thermite based on purescript-sync-websockets
PureScript
3
star
22

atea.vim

Atea vim plugin
Vim Script
3
star
23

hyde

A simple Haskell HTML templating engine
Haskell
2
star
24

quadtree-acid

Haskell quad tree experiments
Haskell
2
star
25

leibniz

Approximating nested GADTs/Leibniz equality
Haskell
2
star
26

tellme_api

API backend for telll.me
Erlang
2
star
27

concur-replica-components

Haskell
2
star
28

tellme

A minimalist 1on1 web chat
Clojure
2
star
29

elm-gen

Autogenerate Elm plumbing
Elm
2
star
30

TC

Haskell
2
star
31

dotfiles

Vim Script
2
star
32

distributed-stm

Haskell
2
star
33

concur-core

Testable Concur core model
Haskell
2
star
34

generative-purescript

Generative art experiments in Purescript
JavaScript
2
star
35

typesafe-query

Haskell
1
star
36

typesafe-query-mongodb

Haskell
1
star
37

purescript-relational

PureScript
1
star
38

jdic-macos-tray

A low-level JNI MacOS menu bar integration for Java
Objective-C
1
star
39

purescript-sync-websockets

Purescript client for sync-websockets
JavaScript
1
star
40

stm-store

Transactional database experiment based on Haskell's STM (and unsafeIOToSTM)
Haskell
1
star
41

users-remote

User auth microservice based on users-postgresql-simple and msgpack
Haskell
1
star
42

reentrant

Experiments related to http://www.reddit.com/r/haskell/comments/344x6d/manage_nonreentrant_blocking_code
Haskell
1
star
43

elm-refactor

Haskell
1
star
44

bptree

B+ tree in Typescript (ported from Haskell)
Haskell
1
star
45

raycaster

JavaScript
1
star
46

react-optics

Haskell
1
star
47

purescript-refract-todomvc

purescript-refract Todo MVC example.
CSS
1
star
48

safedata-aeson

Haskell
1
star
49

channels-websockets

Haskell
1
star
50

ocean

WebGL ocean demo
Java
1
star
51

allseeingeye

Experimental code similarity detection tool
Haskell
1
star
52

sync-websockets

Synchronous websocket communication
Haskell
1
star
53

haskell-ajax-cont

Asynchronous http requests using the continuation monad
Haskell
1
star
54

safedata-bson

Haskell
1
star