Turbine is a purely functional frontend framework powered by FRP.
- Concise and powerful thanks to FRP.
- No big global state/model. Everything is incapsulated in components.
- Type-safe communication between views and models.
- Model logic and view code is kept seperate for logic-less views.
counterModel {increment, decrement} init = do
let changes = (increment $> 1) <> (decrement $> -1)
count <- sample $ scan (+) init changes
pure {count}
counterView {count} =
E.text "Counter " </>
E.span (E.textB $ map show count) </>
E.button "+" `output` (\o -> {increment: o.click}) </>
E.button "-" `output` (\o -> {decrement: o.click})
counter = modelView counterModel counterView
main = runComponent "#mount" (counter 0)
Show a list of counters. New counters can be added to the list. Existing counters can be deleted. The aggregated sum of all the counters is shown.
counterModel {increment, decrement, delete} id = do
let changes = (increment $> 1) <> (decrement $> -1)
count <- sample $ scan (+) 0 changes
pure {count, delete: delete $> id}
counterView {count} =
E.div (
E.text "Counter" </>
E.span (E.textB $ map show count) </>
E.button "+" `output` (\o -> {increment: o.click}) </>
E.button "-" `output` (\o -> {decrement: o.click}) </>
E.button "x" `output` (\o -> {delete: o.click})
)
counter = modelView counterModel counterView
counterListModel {addCounter, listOut} init = do
let sum = listOut >>= (map (_.count) >>> foldr (lift2 (+)) (pure 0))
let removeId = map (fold <<< map (_.delete)) listOut
let removeCounter = map (\i -> filter (i /= _)) (switchStream removeId)
nextId <- sample $ scanS (+) 0 (addCounter $> 1)
let appendCounter = cons <$> nextId
counterIds <- sample $ scan ($) init (appendCounter <> removeCounter)
pure {sum, counterIds}
counterListView {sum, counterIds} =
E.div (
E.h1 (E.text "Counters") </>
E.span (E.textB (map (\n -> "Sum " <> show n) sum)) </>
E.button "Add counter" `output` (\o -> {addCounter: o.click}) </>
list counter counterIds id `output` (\o -> {listOut: o})
)
counterList = modelView counterListModel counterListView
main = runComponent "#mount" (counterList [0])