Concur UI Lib is a brand new client side Web UI framework 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. This repo contains the Concur implementation for Purescript, using the React backend.
Work in progress tutorials are published in the Concur Documentation site
API documentation is published on Pursuit.
Purescript-Concur is reasonably light. The entire uncompressed JS bundle, including react and all libraries, for the entire example application in this repo clocks in at 180KB. You can build this bundle yourself with the command
npm run prod (currently broken due to the move to spago).
This leads to pretty fast initial load times. Running the Chrome audit on https://ajnsit.github.io/purescript-concur/ produces -
Ports to other languages
- Concur for Haskell - The original version of Concur written in Haskell.
You can quickly get a production setup going (using Spago and Parcel) by cloning the Purescript Concur Starter.
Else you can also install purescript-concur manually using bower -
bower install purescript-concur-react
Building examples from source
git clone https://github.com/ajnsit/purescript-concur.git cd purescript-concur npm install # Build source and examples npm run build # Start a local server npm run start # Check examples open localhost:1234 in the browser
External React Components
It's easy to add external React components to Concur. Usually all you would require to wrap an external component is to import it as a
ReactClass, and then wrapping it with one of the
For example, let's say you want to wrap the
Button component provided by the material-ui library.
Step 1: First write an FFI module that exposes the
ReactClass component -
// Button.js exports.classButton = require('@material-ui/core/Button').default
And import it into your purescript program
-- Button.purs foreign import classButton :: forall a. ReactClass a
If you are using the Purescript React MUI bindings, then you can simply import the class component from the library without defining the FFI module -
import MaterialUI.Button (classButton)
Step 2: Then wrap up the imported
ReactClass into a widget to make it usable within Concur -
import Concur.React.DOM (El, el') import React (unsafeCreateElement) import React.DOM.Props (unsafeFromPropsArray) button :: El button = el' (unsafeCreateElement classButton <<< unsafeFromPropsArray)
Step 3: Now you can use
button normally within Concur. For example -
import Concur.React.DOM as D import Concur.React.Props as P helloButton = button [P.onClick] [D.text "Hello World!"]
Note that you can mix in the default widgets and props with the MUI ones.
Individual example sources -
- Hello World! Shows simple effectful widgets with state using StateT. Source.
- A simple counter widget without using StateT. Source.
- Focus counter demonstrates a stateful widget, with multiple event handlers, and no action types needed! Source.
- A login widget. Source.
- Concur has Signals! Sample counting widget implemented with Signals! Source.
- A Full-featured TodoMVC implementation with LocalStorage Persistence built with Signals. Source.
- A Fully editable tree in ~30 lines of code (with Signals). Source.
- A Postfix calculator. Source.
- Using AJAX and handling JSON responses. Source.
- A small widget to Visualise CSS color codes. Source.
- Asynchronous timers which can be cancelled. Source.
- A Routed widget which demonstrates routing. Source.
- The Elm Architecture example demonstrates how Concur subsumes "The Elm Architecture". Source.
- Performance test - A huge list of 50 thousand parallel buttons. This has two variants, fast (uses slightly lower level interface) and slow (idiomatic concur code). Source.
- Tail Recursion demo - Since Concur is purely functional in nature, its primary mode of iteration is via recursion. Purescript in general is NOT stack stafe with tail recursion; It uses tricks like tailRec and tailRecM. However, Concur performs trampolining to make monadic recursion completely stack safe. This example demonstrates that by making a huge number of tail recursive calls in a short span of time. Source.