• Stars
    star
    350
  • Rank 120,750 (Top 3 %)
  • Language PureScript
  • License
    MIT License
  • Created almost 10 years ago
  • Updated almost 2 years ago

Reviews

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

Repository Details

A simple PureScript wrapper for React

purescript-thermite

Pursuit

purescript-thermite is a PureScript wrapper for purescript-react. It does not provide all of the functionality of React, but instead provides a clean API to the most commonly-used parts of its API. It is possible to use purescript-react for more specialized use cases.

Building

bower update
pulp build
pulp test -r cat > html/index.js

You can also now use npm test to run the test command above.

Getting Started

Thermite components are defined in parts:

  • A type of actions, which represents the actions a user can take on our component
  • A type of states, which represents the internal state of our component
  • An initial state
  • A rendering function, which takes the current component state and properties, and creates a HTML document
  • A function which interprets actions, by modifying the state and/or running some (possibly asynchronous) computations

Here is an example. We'll build a component which displays the value of a integer-valued counter.

First of all, we need to import some modules:

import Thermite as T

import React as R
import React.DOM as R
import React.DOM.Props as RP
import ReactDOM as RDOM

In our component, users will be able to take two actions - increment and decrement - which will be represented as buttons later:

data Action = Increment | Decrement

The state of our component is just an integer:

type State = { counter :: Int }

The initial state is zero:

initialState :: State
initialState = { counter: 0 }

Our rendering function uses the React.DOM.* modules to create a HTML document containing a label and two buttons. The buttons' onclick handlers are given functions which generate the correct actions. The dispatch function, which is passed as the first argument to render, can be used to build such a function, by providing an action:

render :: T.Render State _ Action
render dispatch _ state _ =
  [ R.p' [ R.text "Value: "
         , R.text $ show state.counter
         ]
  , R.p' [ R.button [ RP.onClick \_ -> dispatch Increment ]
                    [ R.text "Increment" ]
         , R.button [ RP.onClick \_ -> dispatch Decrement ]
                    [ R.text "Decrement" ]
         ]
  ]

The performAction function interprets actions by passing a function to the state update function, which is responsible for updating the state using record updates:

performAction :: T.PerformAction State _ Action
performAction Increment _ _ = void (T.cotransform (\state -> state { counter = state.counter + 1 }))
performAction Decrement _ _ = void (T.cotransform (\state -> state { counter = state.counter - 1 }))

Note: PerformAction returns a coroutine, which can emit many asynchronous state updates using cotransform. This approach also allows us to create asynchronous and/or chunked action handlers (using AJAX or websockets, for example):

getIncrementValueFromServer :: Aff Int

performAction :: T.PerformAction State _ Action
performAction Increment _ _ = do
  Just amount <- lift getIncrementValueFromServer
  void $ T.cotransform $ \state -> state { counter = state.counter + amount }

With these pieces, we can create a Spec for our component:

spec :: T.Spec State (T.WithChildren ()) Action
spec = T.Spec {performAction, render}

Note that the new purescript-react needs some typechecking assistance for props - Spec needs an extra { children :: Children | props } field in its props, yet that field is not necessary when creating a react element with its props argument.

WithChildren props is just an alias for { children :: Children | props }.

Finally, in main, the defaultMain function from the purescript-thermite-dom library can be used to render our component to the document body by specifying the initial state:

import Thermite.DOM (defaultMain)

main = defaultMain spec (const initialState) "MyComponent" {}

Combining Components

The Spec type is an instance of the Semigroup and Monoid type classes. These instances can be used to combine different components with the same state and action types.

In practice, the state and action types will not always match for the different subcomponents, so Thermite provides combinators for changing these type arguments: focus and foreach. These combinators are heavily inspired by the OpticUI library.

See the example project for examples of these kinds of composition.

focus

focus (and the related functions focusState and match) are used to enlarge the state and action types, to make it possible to embed a component inside a larger component.

focus takes a lens, which identifies the state type as a part of the state type of the larger component, and a prism, which identifies all actions of the smaller component as actions for the larger component. focusState is used when only the state type needs to be changed, and match is used when only the action type needs to be changed.

As a simple example, we can combine two subcomponents by using a Tuple to store both states, and Either to combine both sets of actions:

spec1 :: Spec S1 _ A1
spec2 :: Spec S2 _ A2

spec :: Spec (Tuple S1 S2) _ (Either A1 A2)
spec = focus _1 _Left spec1 <> focus _2 _Right spec2

Here, _1 and _Left embed spec1 inside spec, using the left components of both the state Tuple and the Either type of actions. _2 and _Right similarly embed spec2, using the right components.

focus is responsible for directing the various actions to the correct components, and updating the correct parts of the state.

split

split is used to handle child components which might not be present, for example, when a parent object contains a Maybe state.

type Parent = { child :: Maybe child }

_child :: LensP Parent (Maybe Child)
_child = lens _.child (_ { child = _ })

_ChildAction :: PrismP ParentAction ChildAction

childSpec :: Spec Child _ ChildAction

spec :: Spec Parent _ ParentAction
spec = focus _child _ChildAction $ split _Just childSpec

foreach

Where focus embeds a single subcomponent inside another component, foreach embeds a whole collection of subcomponents.

foreach turns a Spec eff state props action into a Spec eff (List state) props (Tuple Int action). Note that the state type has been wrapped using List, since the component now tracks state for each element of the collection. Also, the action type has been replaced with Tuple Int action. This means that when an action occurs, it is accompanied by the index of the element in the collection which it originated from.

More Repositories

1

purescript-book

Sources for the PureScript book
PureScript
454
star
2

24-days-of-purescript-2016

24 Days of PureScript, 2016
287
star
3

initialround

Web app for interviewing technical candidates in the browser
C#
163
star
4

purescript-sdom

An experiment in replacing the virtual DOM and avoiding diffing
PureScript
161
star
5

purescript-behaviors

A simple push-pull FRP implementation
PureScript
134
star
6

dovetail

A PureScript interpreter in Haskell
Haskell
118
star
7

purescript-purview

A UI library based on the incremental lambda calculus
PureScript
111
star
8

purescript-react-explore

Experiments with comonads for modelling React UIs
PureScript
90
star
9

purescript-incremental-functions

Incremental lambda calculus
PureScript
80
star
10

purescript-foreign-generic

Generic deriving for purescript-foreign
PureScript
63
star
11

purescript-quickserve

Quick HTTP servers
PureScript
60
star
12

haskell-slides

Haskell
50
star
13

purescript-drawing

A data structure for drawings
PureScript
47
star
14

purescript-derive-lenses

A little utility to derive lenses and prisms for data types in PureScript
PureScript
41
star
15

purescript-smash

An "extensible coeffect" system built out of comonads and Day convolution.
PureScript
36
star
16

the-future-is-comonadic

My (rejected) OBT-2018 submission
TeX
35
star
17

purescript-rest

A toolkit for creating REST services with Node and PureScript
PureScript
32
star
18

purescript-jquery

Type declarations for jQuery
PureScript
30
star
19

mu-kanren

A step-by-step MicroKanren evaluator
JavaScript
29
star
20

star-dodge-clone

A partial clone of 2D Star Dodge
PureScript
26
star
21

purescript-safely

A combinator for making any monadic control operator stack-safe
PureScript
26
star
22

24-days-of-purescript-2014

25
star
23

purescript-event

The Event type, extracted from purescript-behaviors
PureScript
23
star
24

purescript-debugger

A simple console debugger for PureScript functions
PureScript
22
star
25

purescript-graphics-vis

A library for interactively creating graphics visualizations
PureScript
22
star
26

typescript-docs

A documentation tool for TypeScript Definition files
Haskell
21
star
27

purescript-behaviors-demo

purescript-behaviors in PSCi
PureScript
19
star
28

language-typescript

Haskell library for working with TypeScript Definition files
Haskell
18
star
29

purescript-leibniz

Leibniz Equality
PureScript
18
star
30

purescript-memoize

Type classes for creating memoized functions
PureScript
18
star
31

lambdaconf-2015

Materials for my LambdaConf 2015 Workshop
PureScript
17
star
32

purescript-yargs

PureScript bindings for the yargs command-line parsing library
PureScript
17
star
33

partial

A nullary type class for partial functions
Haskell
15
star
34

purescript-signal-loop

An abstraction on top of purescript-signal, for avoiding Channels
PureScript
15
star
35

purescript-folds

Applicative Folds, in the style of Gabriel Gonzalez' foldl library
PureScript
14
star
36

purescript-free-canvas

A free monad interface to the canvas
PureScript
13
star
37

purescript-lists-fast

Fast replacements for some common functions on linked lists
PureScript
13
star
38

purescript-hooks

PureScript
12
star
39

Purity

CLR-Hosted Total Functional Programming Language
C#
11
star
40

dicom-haskell-library

A library for reading and writing DICOM files in the Explicit VR Little Endian transfer syntax.
Haskell
10
star
41

purescript-day

Day Convolution
PureScript
10
star
42

runpurs

PureScript corefn interpreter experiment for processing JSON
JavaScript
10
star
43

purescript-hoist

Optics between functors
PureScript
10
star
44

purescript-foreign-lens

A lens-compatible set of getters for purescript-foreign
PureScript
10
star
45

purescript-node-coroutines

Coroutines for working with Node streams
PureScript
9
star
46

lambdaconf

LambdaConf Notes
JavaScript
9
star
47

Embedding-a-Full-Linear-Lambda-Calculus-in-Haskell

Embedding a Full Linear Lambda Calculus in Haskell
8
star
48

Adventure

For the LA Haskell Meetup
Haskell
8
star
49

purescript-scoped-labels

A little implementation of records with duplicate labels
PureScript
8
star
50

purescript-pprint

A simple pretty printing library
PureScript
8
star
51

purescript-bsp-experiment

An isometric canvas renderer which supports transparency
JavaScript
8
star
52

purescript-lens-simple

Very basic lenses
PureScript
7
star
53

purescript-reflection

Reflecting values at the type level
PureScript
7
star
54

stack-safety-for-free

A note on the approach to stack-safety in PureScript's core libraries
PureScript
7
star
55

purescript-tropical

Tropical Semirings
PureScript
6
star
56

purescript-pairing

Pairings of functors
PureScript
6
star
57

codemesh2016

Code Mesh 2016 slides
PureScript
5
star
58

tablestorage

Azure Table Storage REST API Wrapper
Haskell
4
star
59

hasuracon-2022

Haskell
4
star
60

blog-source

Markdown files and static site generator script for my blog.
Haskell
4
star
61

purescript-distributions

A monad which generalizes the probability monad to an arbitrary Semiring of probabilities
PureScript
4
star
62

purescript-isomorphisms

A category of isomorphisms, and some standard isomorphisms
PureScript
4
star
63

HsDiff

Haskell Diff Tool
Haskell
4
star
64

tinytemplate

A tiny text templating library for Haskell
Haskell
3
star
65

purescript-webidl

A wrapper for the webidl2 library
PureScript
3
star
66

Automata-hs

A monad for finite deterministic automata
Haskell
3
star
67

purescript-with-index

A tiny library for composing indexed traversals
PureScript
3
star
68

old-blog

Very old blog posts
2
star
69

supervisor

A simple ptrace-based supervisor
C
2
star
70

BF

BF .NET Compiler
C#
2
star
71

purescript-croco

Croco Magneto remake in PureScript - work in progress
JavaScript
2
star
72

acme-all-monad

A monad which is powerful enough to interpret any action
Haskell
2
star
73

codejam

Google CodeJam Solutions
Haskell
1
star
74

purescript-sammy

PureScript bindings for Sammy.js, a Javascript, browser-based Sinatra-clone.
PureScript
1
star
75

parsel

A just-in-time compiler for recursive descent parsers
C#
1
star
76

paf31.github.io

functorial
HTML
1
star
77

with-index

A tiny library for composing indexed traversals
Haskell
1
star
78

purescript-taylor

Taylor series
PureScript
1
star
79

purescript-drumloops

Experimenting with purescript-howler
PureScript
1
star
80

Automata

A monad for finite deterministic automata
C#
1
star
81

pattern-arrows

Arrows for Pretty Printing
Haskell
1
star
82

monad-unify

A library for type-safe first-order unification
Haskell
1
star
83

purescript-handlebars

Simple bindings to the handlebars text templating library
PureScript
1
star