A brand new client side Web UI framework for Haskell that explores an entirely new paradigm. It does not follow FRP (think Reflex or Reactive Banana), or Elm architecture, but aims to combine the best parts of both.
It has two backends -
There is a Work-In-Progress Extremely-Experimental backend called concur-doom which explores constructing DOM elements once and then editing only the parts that change, without dom-diffing, and while maintaining pure view rendering as well as having the same API as the other virtual-dom based backends.
The eventual aim is to have a consistent API for all backends, so for example, a web app using react can simply be recompiled as a native GUI. Of course, any native react widgets used by the app would have to be ported to the native platform as well.
Access some performance benchmarks here - https://ajnsit.github.io/concur-benchmarks/
Comparison with Elm/Miso and my comment on Reddit on Comparison with ReflexNOTE: I need to write better docs. Until then, there is some good information in my answer on
The UI problem
- JS based UI libs tend to be very unstructured and require a lot of effort from the programmer to keep things maintainable. React is the only exception I've seen, however it too comes with its own complications due to the fact that it is written in JS.
- Elm has a lot of things going for it, but ultimately, "The Elm Architecture" feels very constrained. There are many well known abstractions for structuring code that we would like to use, but can't. For example, we can't use monads to build UIs incrementally. There are no monad transformers, so no using StateT to manage state. Even the composability of multiple widgets suffers because of how rigidly we must adhere to the elm architecture and must loop everything throgh a state modification and a render function. This codebase started out as an attempt to keep Elm's advantages, while opening up new avenues to structuring code.
- Reflex/FRP has brilliant ideas at its core but ultimately suffers from annoying flaws due to its flexibility and complexity. FRP is NOT the easiest model to think and reason about. I've also found it difficult to refactor. For example, if you have to extract a bunch of events from deep within the DOM (though that may just be my bad coding practices). There are unsolved problems (such as handling global popups) that usually necessitate peppering random IO effects throughout the codebase, which can cause race conditions. In my opinion, Reflex is currently the best solution for UIs, in that it gives you the full power of Haskell, and is quite fun to use, but it can be improved upon.
This library was originally conceived as a way to improve Elm by adding -
- Easy widget composition. If you have a
counterwidget, then adding two of them in the same page should be something as simple as
counter <|> counter
- Remove the focus on State modification. Make rendering a
Monadso that it's possible to use
StateTtransformer to get easy state modification back.
- Use inversion of control to make everything seem synchronous and simplify code. Waiting for a click event should be as simple as
click >>= doSomethingwith no callbacks in sight.
- Using inversion of control made effects more expressive, while still not as unstructured as Reflex. An effect in Concur is simply
IO a. If you want to display a widget every 10 seconds, it's as simple as
forever $ liftIO (threadDelay 10*1000000) >> doSomething. If you want to do the same thing every 10 seconds and on every click, it's as simple as
forever $ (getClick <|> liftIO (threadDelay 10*1000000) >>= doSomething.
- Eliminating the difference between state modification and view rendering. So a new widget can be simply -
div (class_ "hello") [text "Here are some widgets", counter, someOtherWidget]. This wires up both the view rendering and the action handling for all nested widgets, without having to manually manage each separately.
- Click Counting Example - Demo - Count total number of clicks on the page, with a button that increments the click count by 10, and also autoincrement clicks every second.
- TodoMVC Example - Demo - The canonical TodoMVC example, with views modeled after the one in Elm.
- Mario Example - Demo - Port of the Mario example from Elm.
- High/Low Game (Virtual-dom) - Demo - An extremely simple number guessing game in 15 lines of code.
- High/Low Game (React) - Demo - The same HiLo game, using the React backend.
- Kirby Super Star Ultra Splits Timer GUI Challange - Demo - Concur implementation of the KSSU Splits Timer GUI Challenge. Shows a moderately complex UI that's not a todolist!
- Menu Widget (React) - Demo - Builds a generic menu widget in 10 lines of code.
- Drag Drop Sortable List Widget (React) - Demo - Demonstrates Concur binding to React-Sortable-Tree. A good example of reusing existing React components in Concur.
- Your first 8 Concur Pipes Widgets (React) - Demo - Your first 8 Concur Pipe programs! Inspired from the mighty Fudgets' - http://www.altocumulus.org/Fudgets/Intro/