PureScript and Pux Workshop for JSConf Medellin
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src
test
.gitignore
README.md
index.html
psc-package.json

README.md

PureScript Workshop for JSConf Medellín

Lesson 1: Getting Started

  1. Install psc-package and pulp
npm i -g psc-package pulp
  1. Initialize the project
pulp --psc-package init
  1. Build the project
pulp --psc-package run
  1. Do your happy dance!
My first PureScript project 🕺

Part 2: Resources and Creating a Bundle

Resources

  1. PureScript by Example (Free eBook)
  2. Let's Build a Simon Game in PureScript
  3. PureScript Cheatsheet
  4. PureScript Slack Channel

Bundle

  1. Creat an index.html at the root of your directory
touch index.html
  1. 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>
  1. Run the server
pulp server
  1. Do your happy dance!
My first PureScript app 💃

Part 3: Hello Pux!

  1. Install Pux
psc-package install pux
  1. 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, (#!))
  1. User Actions
data Event = Increment | Decrement
  1. State
type State = Int
  1. Update
foldp ::  fx. Event -> State -> EffModel State Event fx
foldp Increment n = { state: n + 1, effects: [] }
foldp Decrement n = { state: n - 1, effects: [] }
  1. 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"
  1. Main
main ::  fx. Eff (CoreEffects fx) Unit
main = do
  app <- start
    { initialState: 0
    , view
    , foldp
    , inputs: []
    }

  renderToDOM "#app" app.markup app.input
  1. 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>
  1. Bundle and Serve
pulp server
  1. Do your happy dance!
My first PureScript app that does something 🕺 💃

Part 4: Add Effects

  1. Install the aff module
psc-package install aff
  1. Import modules
import Control.Monad.Aff.Console (CONSOLE, log)
import Data.Maybe (Maybe(..)) -- Part of Prelude
  1. Define App Effects
type AppEffects = ( console:: CONSOLE )
  1. 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
]}
  1. Update Main
main ::  fx. Eff (CoreEffects AppEffects) Unit -- 👈
main = do
  app <- start
    { initialState: 0
    , view
    , foldp
    , inputs: []
    }

  renderToDOM "#app" app.markup app.input
  1. Run server
pulp server
  1. Do your happy dance!
My first PureScript side-effect 🕺

Part 5: HTTP Side-Effect

  1. Install necessary modules
psc-package install argonaut-codecs affjax
  1. 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, (#!), (!))
  1. Define effects
type AppEffects = ( console:: CONSOLE, ajax:: AJAX )
  1. Define user actions
data Event = RequestGiphy | ReceiveGiphy (Either String Url)
  1. Define a newtype (for decoding)
newtype Url = Url String

derive instance newtypeUrl :: Newtype Url _

unwrap :: Url -> String
unwrap = un Url
  1. Define the state
type State = Url
  1. Decoder
instance decodeJsonUrl :: DecodeJson Url where
  decodeJson json = do
    obj <- decodeJson json
    info <- obj .? "data"
    imgUrl <- info .? "image_original_url"
    pure $ Url imgUrl
  1. 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 ]}
  1. View
view url =
  div do
    button #! onClick (const RequestGiphy) $ text "Get Random Giphy"
    img ! src (unwrap url)
  1. Main
main ::  fx. Eff (CoreEffects AppEffects) Unit
main = do
  app <- start
    { initialState: Url "" -- 👈
    , view
    , foldp
    , inputs: []
    }

  renderToDOM "#app" app.markup app.input
  1. Run server
pulp server
  1. Do your happy dance!
My first PureScript HTTP request 🕺 💃

Part 6: Make request from user input

  1. 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)
  1. Update state
type State =
  { url :: Url
  , input :: String
  }
  1. 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: [] }
  1. 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)
  1. Main
main ::  fx. Eff (CoreEffects AppEffects) Unit
main = do
  app <- start
    { initialState: { input: "", url: Url "" } -- 👈
    , view
    , foldp
    , inputs: []
    }

  renderToDOM "#app" app.markup app.input
  1. Run server
pulp server
  1. Do your happy dance!
My first PureScript Giphy App 💃