UI Programming with
PureScript and Halogen
Moritz Drexl
Hamburg Haskell Meetup
Jan 27, 2016
PureScript
Differences from Haskell
https://github.com/purescript/purescript/wiki/Differences-from-Haskell
PureScript is a small strongly typed programming language that compiles to JavaScript.
- Strict evaluation
- No type families and functional dependencies
- Not all Language Extensions, but included
- RankNTypes
- ScopedTypeVariables
- FlexibleContexts, FlexibleInstances
- ...
- Better, more granular typeclass hierarchy
(no (>>), mapM, ...)
PureScript
Differences from Haskell
https://github.com/purescript/purescript/wiki/Differences-from-Haskell
-- Explicit forall; Number & Int; no syntactic sugar for Lists
length :: forall a. Array a -> Number
-- No syntactic sugar like (), (a, b)
foo :: Tuple Int String -> State Unit
-- Different composition operator
count = sum <<< map length
-- Named instances
instance arbitraryUnit :: Arbitrary Unit where
-- Superclass semantic arrows
class (Eq a) <= Ord a where
...
PureScript is a small strongly typed programming language that compiles to JavaScript.
PureScript
Differences from Haskell
https://github.com/purescript/purescript/wiki/Differences-from-Haskell
-- Javascript-Style Objects
type Point = { x :: Number, y :: Number }
origin :: Point
origin = { x: 0, y: 0 }
getX :: Point -> Number
getX p = p.x
-- Extensible records
fullName :: forall r. { firstName :: String, lastName :: String | r } -> String
fullName person = person.firstName ++ " " ++ person.lastName
PureScript is a small strongly typed programming language that compiles to JavaScript.
PureScript
Differences from Haskell
https://github.com/purescript/purescript/wiki/Differences-from-Haskell
-- Extensible effects instead of IO
main :: Eff (console :: CONSOLE, dom :: DOM) Unit
main = do
logSomething
attachSpanTo "#myElement"
logSomething :: forall e. Eff (console :: CONSOLE | e) Unit
attachSpanTo :: forall e. String -> Eff (dom :: DOM | e) Unit
PureScript is a small strongly typed programming language that compiles to JavaScript.
PureScript
Foreign Function Interface
module Test where
add :: Int -> Int -> Int
add a b = a + b
var Test = require('Test');
Test.add(15)(20);
module Test where
foreign import add :: Number -> Number -> Number
main :: Eff (console :: CONSOLE) Unit
main = log $ add 4 5
"use strict";
// module Test
exports.add = function(a) {
return function(b) {
return a + b;
};
};
Call PureScript from JS:
Call JS from PureScript:
PureScript
Resources
PureScript is a small strongly typed programming language that compiles to JavaScript.
Documentation: pursuit.purescript.org
Homepage: purescript.org
Halogen
App Structure
- A Halogen app is a tree of components/widgets
- Each component defines
- A component state
- A render function :: State -> HTML
- A set of queries to the component
- An eval function that interprets the inputs
(~ Query -> State + Effects Free Monad)
- Components can
- communicate with children
- subscribe to events (3rd party widgets)
- Components are composed in a type-safe manner
Halogen
Example
type State = { on :: Boolean }
data Query a
= ToggleState a
| GetState (Boolean -> a)
myComponent :: forall g. (Functor g) => Component State Query g
myComponent = component render eval
where
render :: State -> ComponentHTML Query
render state =
H.div_
[ H.h1_
[ H.text "Toggle Button" ]
, H.button
[ E.onClick (E.input_ ToggleState) ]
[ H.text (if state.on then "On" else "Off") ]
]
eval :: Eval Query State Query g
eval (ToggleState next) = do
modify (\state -> { on: not state.on })
pure next
eval (GetState continue) = do
value <- gets _.on
pure (continue value)
Halogen
Parent Child Relationship
data TickSlot = LeftSlot | RightSlot
instance eqTickSlot :: Eq TickSlot where eq = ...
instance ordTickSlot :: Ord TickSlot where compare = ...
render :: State -> ParentHTML TickState Query TickQuery g TickSlot
render st = H.div_
[ H.slot LeftSlot \_ -> { component: ticker, initialState: TickState 100 }
, H.slot RightSlot \_ -> { component: ticker, initialState: TickState 0 }
]
eval :: EvalParent Query State TickState Query TickQuery g TickSlot
eval (ReadTicks next) = do
a <- query LeftSlot (request GetTick)
...
pure next
Children are inserted into and referenced by "Slots"
Halogen
Parent Child Communication
peek :: Peek State TickState Query TickQuery g TickSlot p
peek (ChildF p q) = case q of
Tick _ -> do
a <- query p (request GetTick)
when (a > 100) $ modify alertOn
_ -> pure unit
Initiated by Parent: Request and Action
eval :: EvalParent Query State TickState Query TickQuery g TickSlot
eval (ReadTicks next) = do
a <- query LeftSlot (request GetTick)
query RightSlot (action $ SetTick a)
pure next
Initiated by Child: Peek
(Can also directly peek at grand-children)
(Children can pass actions along to grand-children)
Peek
Request/
Action
Halogen
Metrix Frontend Components
App
Spinner
ErrorBox
Body
FileSelector
FileViewer
File
ModuleBrowser
Handsontable Wrapper
Validation
FileMenu
Halogen
Metrix Frontend Components
Live Code
End
PureScript and Halogen
By Moritz Drexl
PureScript and Halogen
- 719