PureScript Workshop for JSConf Medellín
Lesson 1: Getting Started
- Install psc-package and pulp
npm i -g psc-package pulp
- Initialize the project
- Build the project
- Do your happy dance!
My first PureScript project 🕺
Part 2: Resources and Creating a Bundle
- PureScript by Example (Free eBook)
- Let's Build a Simon Game in PureScript
- PureScript Cheatsheet
- PureScript Slack Channel
- Creat an
index.html
at the root of your directory
- Add basic
html
markup
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script src='./output/app.js'></script>
</body>
</html>
- Run the server
- Do your happy dance!
My first PureScript app 💃
- Install Pux
- Imports
import Prelude hiding (div)
import Control.Monad.Eff (Eff)
import Pux (CoreEffects, EffModel, start)
import Pux.DOM.Events (onClick)
import Pux.DOM.HTML (HTML)
import Pux.Renderer.React (renderToDOM)
import Text.Smolder.HTML (button, div, span)
import Text.Smolder.Markup (text, (#!))
- User Actions
data Event = Increment | Decrement
- State
- Update
foldp :: ∀ fx. Event -> State -> EffModel State Event fx
foldp Increment n = { state: n + 1, effects: [] }
foldp Decrement n = { state: n - 1, effects: [] }
- View
view :: State -> HTML Event
view count =
div do
button #! onClick (const Increment) $ text "Increment"
span $ text (show count)
button #! onClick (const Decrement) $ text "Decrement"
- Main
main :: ∀ fx. Eff (CoreEffects fx) Unit
main = do
app <- start
{ initialState: 0
, view
, foldp
, inputs: []
}
renderToDOM "#app" app.markup app.input
- Mount App and Add React
<body>
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<script src='./output/app.js'></script>
</body>
- Bundle and Serve
- Do your happy dance!
My first PureScript app that does something 🕺 💃
- Install the aff module
- Import modules
import Control.Monad.Aff.Console (CONSOLE, log)
import Data.Maybe (Maybe(..)) -- Part of Prelude
- Define App Effects
type AppEffects = ( console:: CONSOLE )
- Log state to console
foldp :: ∀ fx. Event -> State -> EffModel State Event AppEffects -- 👈
foldp Increment n = { state: n + 1, effects: [
do
log $ "Current State: " <> (show $ n + 1)
pure Nothing
]}
foldp Decrement n = { state: n - 1, effects: [
do
log $ "Current State: " <> (show $ n - 1)
pure Nothing
]}
- Update Main
main :: ∀ fx. Eff (CoreEffects AppEffects) Unit -- 👈
main = do
app <- start
{ initialState: 0
, view
, foldp
, inputs: []
}
renderToDOM "#app" app.markup app.input
- Run server
- Do your happy dance!
My first PureScript side-effect 🕺
- Install necessary modules
psc-package install argonaut-codecs affjax
- Import modules
import Control.Monad.Aff (attempt)
import Data.Argonaut.Decode (decodeJson, (.?))
import Data.Argonaut.Decode.Class (class DecodeJson)
import Data.Either (Either(Left, Right), either)
import Data.Newtype (class Newtype, un)
import Network.HTTP.Affjax (AJAX, get)
import Text.Smolder.HTML (button, div, img)
import Text.Smolder.HTML.Attributes (src)
import Text.Smolder.Markup (text, (#!), (!))
- Define effects
type AppEffects = ( console:: CONSOLE, ajax:: AJAX )
- Define user actions
data Event = RequestGiphy | ReceiveGiphy (Either String Url)
- Define a newtype (for decoding)
newtype Url = Url String
derive instance newtypeUrl :: Newtype Url _
unwrap :: Url -> String
unwrap = un Url
- Define the state
- Decoder
instance decodeJsonUrl :: DecodeJson Url where
decodeJson json = do
obj <- decodeJson json
info <- obj .? "data"
imgUrl <- info .? "image_original_url"
pure $ Url imgUrl
- Update
foldp :: ∀ fx. Event -> State -> EffModel State Event AppEffects
foldp RequestGiphy state = { state: state, effects: [
do
result <- attempt $ get "https://api.giphy.com/v1/gifs/random?api_key=670526ba3bda46629f097f67890105ed&tag=&rating=G"
let decode res = decodeJson res.response :: Either String Url
let url = either (Left <<< show) decode result
pure $ Just $ ReceiveGiphy url
]}
foldp (ReceiveGiphy (Left _)) state = { state: state, effects: [ log "Error" *> pure Nothing ] }
foldp (ReceiveGiphy (Right url)) state = { state: url, effects: [ log "ReceivedGiphy" *> pure Nothing ]}
- View
view url =
div do
button #! onClick (const RequestGiphy) $ text "Get Random Giphy"
img ! src (unwrap url)
- Main
main :: ∀ fx. Eff (CoreEffects AppEffects) Unit
main = do
app <- start
{ initialState: Url "" -- 👈
, view
, foldp
, inputs: []
}
renderToDOM "#app" app.markup app.input
- Run server
- Do your happy dance!
My first PureScript HTTP request 🕺 💃
Part 6: Make request from user input
- Update Imports
import Pux.DOM.Events (onClick, onChange, DOMEvent, targetValue)
import Text.Smolder.HTML (button, div, img, input)
import Text.Smolder.HTML.Attributes (src, type', value)
- Update state
type State =
{ url :: Url
, input :: String
}
- Update
foldp :: ∀ fx. Event -> State -> EffModel State Event AppEffects
foldp RequestGiphy state = { state: state, effects: [
do
result <- attempt $ get $ "https://api.giphy.com/v1/gifs/random?api_key=670526ba3bda46629f097f67890105ed&tag=" <> state.input <> "&rating=G"
let decode res = decodeJson res.response :: Either String Url
let url = either (Left <<< show) decode result
pure $ Just $ ReceiveGiphy url
]}
foldp (ReceiveGiphy (Left _)) state = { state: state, effects: [ log "Error" *> pure Nothing ] }
foldp (ReceiveGiphy (Right url)) state = { state: state { url = url }, effects: [ log "ReceivedGiphy" *> pure Nothing ]}
foldp (UserInput ev) state = { state: state { input = targetValue ev }, effects: [] }
- View
view state =
div do
input ! type' "text" #! onChange UserInput ! value state.input
button #! onClick (const RequestGiphy) $ text "Get Random Giphy"
img ! src (unwrap state.url)
- Main
main :: ∀ fx. Eff (CoreEffects AppEffects) Unit
main = do
app <- start
{ initialState: { input: "", url: Url "" } -- 👈
, view
, foldp
, inputs: []
}
renderToDOM "#app" app.markup app.input
- Run server
- Do your happy dance!
My first PureScript Giphy App 💃