33 | ^{widget}
34 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | TEX=pdflatex
2 |
3 | MASTER=src/master.tex
4 | TEX_OPTIONS=options.tex
5 | SRCS=$(shell find src -name '*.tex') \
6 | $(shell find src -name '*.bib') \
7 | $(shell find src/listings) \
8 | src/slides.tex
9 | IMGS=$(shell find src -name '*.png')
10 |
11 | HS_SRCS=$(shell find src/listings/scotty-demo -name '*.hs') \
12 | src/listings/scotty-demo/scotty-demo.cabal \
13 | src/listings/scotty-demo/stack.yaml
14 |
15 | PANDOC_FLAGS= -t beamer \
16 | -f markdown+multiline_tables \
17 | -s \
18 | -H src/customizations.tex \
19 | -Vurlcolor=linkcolor \
20 | --highlight-style=haddock \
21 | --slide-level=2 \
22 | --filter pandoc-include-code \
23 | -fmarkdown-implicit_figures \
24 |
25 | SLIDES_DIR=target/slides
26 | SLIDES=$(SLIDES_DIR)/slides.pdf
27 |
28 | SLIDES_NO_NOTES_DIR=target/slides-no-notes
29 | SLIDES_NO_NOTES=$(SLIDES_NO_NOTES_DIR)/slides-no-notes.pdf
30 |
31 | .PHONY: all
32 | all: slides programs
33 |
34 | .PHONY: slides
35 | slides: $(SLIDES) $(SLIDES_NO_NOTES)
36 |
37 | target/slides.tex: src/slides.md src/customizations.tex src/notes.tex $(HS_SRCS) $(IMGS)
38 | mkdir -p target
39 | pandoc $(PANDOC_FLAGS) \
40 | -H src/notes.tex \
41 | $< \
42 | -o $@
43 |
44 | target/slides-no-notes.tex: src/slides.md src/customizations.tex
45 | mkdir -p target
46 | pandoc $(PANDOC_FLAGS) -V classoption=handout $< -o $@
47 |
48 | $(SLIDES): target/slides.tex
49 | rm -rf $(SLIDES_DIR)
50 | mkdir -p $(SLIDES_DIR)
51 | cp target/slides.tex $(SLIDES_DIR)/slides.tex
52 | cd $(SLIDES_DIR) && \
53 | $(TEX) \
54 | -jobname slides \
55 | -halt-on-error \
56 | slides.tex
57 |
58 | $(SLIDES_NO_NOTES): target/slides-no-notes.tex
59 | rm -rf $(SLIDES_NO_NOTES_DIR)
60 | mkdir -p $(SLIDES_NO_NOTES_DIR)
61 | cp target/slides-no-notes.tex $(SLIDES_NO_NOTES_DIR)/slides.tex
62 | cd $(SLIDES_NO_NOTES_DIR) && \
63 | $(TEX) \
64 | -jobname slides-no-notes \
65 | -halt-on-error \
66 | slides.tex
67 |
68 | programs:
69 | cd src/listings/scotty-demo && cabal new-build
70 | cd src/listings/yesod-demo && stack build
71 | cd src/listings/airship-demo && cabal new-build
72 |
73 | clean:
74 | rm -rf target
75 |
--------------------------------------------------------------------------------
/src/listings/yesod-demo/README.md:
--------------------------------------------------------------------------------
1 | ## Haskell Setup
2 |
3 | 1. If you haven't already, [install Stack](https://haskell-lang.org/get-started)
4 | * On POSIX systems, this is usually `curl -sSL https://get.haskellstack.org/ | sh`
5 | 2. Install the `yesod` command line tool: `stack install yesod-bin --install-ghc`
6 | 3. Build libraries: `stack build`
7 |
8 | If you have trouble, refer to the [Yesod Quickstart guide](https://www.yesodweb.com/page/quickstart) for additional detail.
9 |
10 | ## Development
11 |
12 | Start a development server with:
13 |
14 | ```
15 | stack exec -- yesod devel
16 | ```
17 |
18 | As your code changes, your site will be automatically be recompiled and redeployed to localhost.
19 |
20 | ## Tests
21 |
22 | ```
23 | stack test --flag yesod-demo:library-only --flag yesod-demo:dev
24 | ```
25 |
26 | (Because `yesod devel` passes the `library-only` and `dev` flags, matching those flags means you don't need to recompile between tests and development, and it disables optimization to speed up your test compile times).
27 |
28 | ## Documentation
29 |
30 | * Read the [Yesod Book](https://www.yesodweb.com/book) online for free
31 | * Check [Stackage](http://stackage.org/) for documentation on the packages in your LTS Haskell version, or [search it using Hoogle](https://www.stackage.org/lts/hoogle?q=). Tip: Your LTS version is in your `stack.yaml` file.
32 | * For local documentation, use:
33 | * `stack haddock --open` to generate Haddock documentation for your dependencies, and open that documentation in a browser
34 | * `stack hoogle
` to generate a Hoogle database and search for your query
35 | * The [Yesod cookbook](https://github.com/yesodweb/yesod-cookbook) has sample code for various needs
36 |
37 | ## Getting Help
38 |
39 | * Ask questions on [Stack Overflow, using the Yesod or Haskell tags](https://stackoverflow.com/questions/tagged/yesod+haskell)
40 | * Ask the [Yesod Google Group](https://groups.google.com/forum/#!forum/yesodweb)
41 | * There are several chatrooms you can ask for help:
42 | * For IRC, try Freenode#yesod and Freenode#haskell
43 | * [Functional Programming Slack](https://fpchat-invite.herokuapp.com/), in the #haskell, #haskell-beginners, or #yesod channels.
44 |
--------------------------------------------------------------------------------
/src/listings/yesod-demo/config/keter.yml:
--------------------------------------------------------------------------------
1 | # After you've edited this file, remove the following line to allow
2 | # `yesod keter` to build your bundle.
3 | user-edited: false
4 |
5 | # A Keter app is composed of 1 or more stanzas. The main stanza will define our
6 | # web application. See the Keter documentation for more information on
7 | # available stanzas.
8 | stanzas:
9 |
10 | # Your Yesod application.
11 | - type: webapp
12 |
13 | # Name of your executable. You are unlikely to need to change this.
14 | # Note that all file paths are relative to the keter.yml file.
15 | #
16 | # The path given is for Stack projects. If you're still using cabal, change
17 | # to
18 | # exec: ../dist/build/yesod-demo/yesod-demo
19 | exec: ../dist/bin/yesod-demo
20 |
21 | # Command line options passed to your application.
22 | args: []
23 |
24 | hosts:
25 | # You can specify one or more hostnames for your application to respond
26 | # to. The primary hostname will be used for generating your application
27 | # root.
28 | - www.yesod-demo.com
29 |
30 | # Enable to force Keter to redirect to https
31 | # Can be added to any stanza
32 | requires-secure: false
33 |
34 | # Static files.
35 | - type: static-files
36 | hosts:
37 | - static.yesod-demo.com
38 | root: ../static
39 |
40 | # Uncomment to turn on directory listings.
41 | # directory-listing: true
42 |
43 | # Redirect plain domain name to www.
44 | - type: redirect
45 |
46 | hosts:
47 | - yesod-demo.com
48 | actions:
49 | - host: www.yesod-demo.com
50 | # secure: false
51 | # port: 80
52 |
53 | # Uncomment to switch to a non-permanent redirect.
54 | # status: 303
55 |
56 | # Use the following to automatically copy your bundle upon creation via `yesod
57 | # keter`. Uses `scp` internally, so you can set it to a remote destination
58 | # copy-to: user@host:/opt/keter/incoming/
59 |
60 | # You can pass arguments to `scp` used above. This example limits bandwidth to
61 | # 1024 Kbit/s and uses port 2222 instead of the default 22
62 | # copy-to-args:
63 | # - "-l 1024"
64 | # - "-P 2222"
65 |
66 | # If you would like to have Keter automatically create a PostgreSQL database
67 | # and set appropriate environment variables for it to be discovered, uncomment
68 | # the following line.
69 | # plugins:
70 | # postgres: true
71 |
--------------------------------------------------------------------------------
/outline.org:
--------------------------------------------------------------------------------
1 | #+TITLE: Fast and Fearless Evolution of Server-Side Web Applications
2 |
3 | * Abstract
4 |
5 | When evolving web applications, in most programming languages and
6 | frameworks, we risk introducing programming errors. Undefined values,
7 | parsing failures, broken links, invalid markup, and good old null
8 | pointers, are all things that can break our applications. Manually
9 | writing and maintaining tests to catch programming errors is a time
10 | consuming effort, and we would rather spend that time testing our
11 | application logic. This talk takes you on a whirlwind tour of mature
12 | technologies that offer static guarantees for modern web applications.
13 |
14 | * Key Points
15 |
16 | - Introduction
17 | - Reasons for change
18 | - New features
19 | - Bug fixes
20 | - Refactorings
21 | - External factors (regulation, deprecations)
22 | - Deadlines
23 | - Risk
24 | - Delay
25 | - Error
26 | - Burnout
27 | - Fast feedback!
28 | - Web applications
29 | - Many of us work with them
30 | - The buzz: SPAs and frameworks
31 | - More like desktop apps
32 | - Reinventing the browser
33 | - No JS, no page
34 | - Server-side apps
35 | - Progressive enhancement
36 | - 80/20 rule
37 | - "Isomorphic" webapps
38 | - PJAX
39 | - Newer /= Better
40 | - Static Typing for Server-side web
41 | - Compile-time guarantees
42 | - Safely evolvable code
43 | - Focus your tests on your domain
44 | - (Examples of functional statically typed language web frameworks)
45 | - Haskell Web Applications
46 | - WAI
47 | - Interface
48 | - Streaming
49 | - Middleware
50 | - Warp
51 | - Web server
52 | - Fast
53 | - Frameworks/libraries
54 | - Scotty
55 | - Compare to Sinatra?
56 | - Build-your-own-setup
57 | - Yesod
58 | - Routing
59 | - Templates
60 | - Forms
61 | - Persistent
62 | - Widgets
63 | - TypeScript
64 | - (Happstack, Snap)
65 | - Airship
66 | - Servant
67 | - MFlow
68 | - Libraries
69 | - Markup
70 | - Blaze HTML
71 | - Lucid
72 | - CSS
73 | - Lucius
74 | - Cassius
75 | - Clay
76 | - Amazonka
77 | - Deployment
78 | - Heroku/Halcyon
79 | - AWS Elastic Beanstalk + Docker
80 | - Others?
81 | - If you need client-side code
82 | - Elm, PureScript, GHCJS
83 | - Servant
84 | - purescript-bridge
85 |
--------------------------------------------------------------------------------
/src/listings/scotty-demo/stack.yaml:
--------------------------------------------------------------------------------
1 | # This file was automatically generated by 'stack init'
2 | #
3 | # Some commonly used options have been documented as comments in this file.
4 | # For advanced use and comprehensive documentation of the format, please see:
5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/
6 |
7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version.
8 | # A snapshot resolver dictates the compiler version and the set of packages
9 | # to be used for project dependencies. For example:
10 | #
11 | # resolver: lts-3.5
12 | # resolver: nightly-2015-09-21
13 | # resolver: ghc-7.10.2
14 | # resolver: ghcjs-0.1.0_ghc-7.10.2
15 | # resolver:
16 | # name: custom-snapshot
17 | # location: "./custom-snapshot.yaml"
18 | resolver: lts-9.11
19 |
20 | # User packages to be built.
21 | # Various formats can be used as shown in the example below.
22 | #
23 | # packages:
24 | # - some-directory
25 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz
26 | # - location:
27 | # git: https://github.com/commercialhaskell/stack.git
28 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
29 | # - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
30 | # extra-dep: true
31 | # subdirs:
32 | # - auto-update
33 | # - wai
34 | #
35 | # A package marked 'extra-dep: true' will only be built if demanded by a
36 | # non-dependency (i.e. a user package), and its test suites and benchmarks
37 | # will not be run. This is useful for tweaking upstream packages.
38 | packages:
39 | - .
40 | # Dependency packages to be pulled from upstream that are not in the resolver
41 | # (e.g., acme-missiles-0.3)
42 | extra-deps: []
43 |
44 | # Override default flag values for local packages and extra-deps
45 | flags: {}
46 |
47 | # Extra package databases containing global packages
48 | extra-package-dbs: []
49 |
50 | # Control whether we use the GHC we find on the path
51 | # system-ghc: true
52 | #
53 | # Require a specific version of stack, using version ranges
54 | # require-stack-version: -any # Default
55 | # require-stack-version: ">=1.5"
56 | #
57 | # Override the architecture used by stack, especially useful on Windows
58 | # arch: i386
59 | # arch: x86_64
60 | #
61 | # Extra directories used by stack for building
62 | # extra-include-dirs: [/path/to/dir]
63 | # extra-lib-dirs: [/path/to/dir]
64 | #
65 | # Allow a newer minor version of GHC than the snapshot specifies
66 | # compiler-check: newer-minor
--------------------------------------------------------------------------------
/src/listings/yesod-demo/stack.yaml:
--------------------------------------------------------------------------------
1 | # This file was automatically generated by 'stack init'
2 | #
3 | # Some commonly used options have been documented as comments in this file.
4 | # For advanced use and comprehensive documentation of the format, please see:
5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/
6 |
7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version.
8 | # A snapshot resolver dictates the compiler version and the set of packages
9 | # to be used for project dependencies. For example:
10 | #
11 | # resolver: lts-3.5
12 | # resolver: nightly-2015-09-21
13 | # resolver: ghc-7.10.2
14 | # resolver: ghcjs-0.1.0_ghc-7.10.2
15 | # resolver:
16 | # name: custom-snapshot
17 | # location: "./custom-snapshot.yaml"
18 | resolver: lts-9.17
19 |
20 | # User packages to be built.
21 | # Various formats can be used as shown in the example below.
22 | #
23 | # packages:
24 | # - some-directory
25 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz
26 | # - location:
27 | # git: https://github.com/commercialhaskell/stack.git
28 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
29 | # - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
30 | # extra-dep: true
31 | # subdirs:
32 | # - auto-update
33 | # - wai
34 | #
35 | # A package marked 'extra-dep: true' will only be built if demanded by a
36 | # non-dependency (i.e. a user package), and its test suites and benchmarks
37 | # will not be run. This is useful for tweaking upstream packages.
38 | packages:
39 | - .
40 | # Dependency packages to be pulled from upstream that are not in the resolver
41 | # (e.g., acme-missiles-0.3)
42 | extra-deps: []
43 |
44 | # Override default flag values for local packages and extra-deps
45 | flags: {}
46 |
47 | # Extra package databases containing global packages
48 | extra-package-dbs: []
49 |
50 | # Control whether we use the GHC we find on the path
51 | # system-ghc: true
52 | #
53 | # Require a specific version of stack, using version ranges
54 | # require-stack-version: -any # Default
55 | # require-stack-version: ">=1.5"
56 | #
57 | # Override the architecture used by stack, especially useful on Windows
58 | # arch: i386
59 | # arch: x86_64
60 | #
61 | # Extra directories used by stack for building
62 | # extra-include-dirs: [/path/to/dir]
63 | # extra-lib-dirs: [/path/to/dir]
64 | #
65 | # Allow a newer minor version of GHC than the snapshot specifies
66 | # compiler-check: newer-minor
--------------------------------------------------------------------------------
/src/listings/yesod-demo/package.yaml:
--------------------------------------------------------------------------------
1 | name: yesod-demo
2 | version: "0.0.0"
3 |
4 | dependencies:
5 |
6 | # Due to a bug in GHC 8.0.1, we block its usage
7 | # See: https://ghc.haskell.org/trac/ghc/ticket/12130
8 | - base >=4.8.2.0 && <4.9 || >=4.9.1.0 && <5
9 |
10 | # version 1.0 had a bug in reexporting Handler, causing trouble
11 | - classy-prelude-yesod >=0.10.2 && <1.0 || >=1.1
12 |
13 | - yesod >=1.4.3 && <1.5
14 | - yesod-core >=1.4.30 && <1.5
15 | - yesod-static >=1.4.0.3 && <1.6
16 | - yesod-form >=1.4.0 && <1.5
17 | - classy-prelude >=0.10.2
18 | - classy-prelude-conduit >=0.10.2
19 | - bytestring >=0.9 && <0.11
20 | - text >=0.11 && <2.0
21 | - template-haskell
22 | - shakespeare >=2.0 && <2.1
23 | - hjsmin >=0.1 && <0.3
24 | - monad-control >=0.3 && <1.1
25 | - wai-extra >=3.0 && <3.1
26 | - yaml >=0.8 && <0.9
27 | - http-conduit >=2.1 && <2.3
28 | - directory >=1.1 && <1.4
29 | - warp >=3.0 && <3.3
30 | - data-default
31 | - aeson >=0.6 && <1.3
32 | - conduit >=1.0 && <2.0
33 | - monad-logger >=0.3 && <0.4
34 | - fast-logger >=2.2 && <2.5
35 | - wai-logger >=2.2 && <2.4
36 | - foreign-store
37 | - file-embed
38 | - safe
39 | - unordered-containers
40 | - containers
41 | - vector
42 | - time
43 | - case-insensitive
44 | - wai
45 | - blaze-markup
46 | - blaze-html
47 |
48 | # The library contains all of our application code. The executable
49 | # defined below is just a thin wrapper.
50 | library:
51 | source-dirs: src
52 | when:
53 | - condition: (flag(dev)) || (flag(library-only))
54 | then:
55 | ghc-options:
56 | - -Wall
57 | - -fwarn-tabs
58 | - -O0
59 | cpp-options: -DDEVELOPMENT
60 | else:
61 | ghc-options:
62 | - -Wall
63 | - -fwarn-tabs
64 | - -O2
65 |
66 | # Runnable executable for our application
67 | executables:
68 | yesod-demo:
69 | main: main.hs
70 | source-dirs: app
71 | ghc-options:
72 | - -threaded
73 | - -rtsopts
74 | - -with-rtsopts=-N
75 | dependencies:
76 | - yesod-demo
77 | when:
78 | - condition: flag(library-only)
79 | buildable: false
80 |
81 | # Test suite
82 | tests:
83 | test:
84 | main: Spec.hs
85 | source-dirs: test
86 | ghc-options: -Wall
87 | dependencies:
88 | - yesod-demo
89 | - hspec >=2.0.0
90 | - yesod-test
91 |
92 | # Define flags used by "yesod devel" to make compilation faster
93 | flags:
94 | library-only:
95 | description: Build for use with "yesod devel"
96 | manual: false
97 | default: false
98 | dev:
99 | description: Turn on development settings, like auto-reload templates.
100 | manual: false
101 | default: false
102 |
--------------------------------------------------------------------------------
/src/listings/scotty-demo/src/Scotty.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Scotty where
3 |
4 | import Control.Monad.IO.Class
5 | import Data.Semigroup
6 | import Data.Text.Lazy
7 | import Web.Scotty
8 | import Lucid
9 |
10 | -- start snippet app
11 | app :: ScottyM ()
12 | app = do
13 |
14 | get "/" $
15 | html "Welcome!"
16 |
17 | get "/greet/:who" $ do
18 | who <- param "who"
19 | html ("Hello, " <> who <> "!")
20 | -- end snippet app
21 | messyHtml
22 | lucidHandler
23 | articleHandler
24 |
25 | -- start snippet lucid-template
26 | homeView :: Text -> Html ()
27 | homeView who =
28 | html_ [lang_ "en"] $ do
29 | head_ $ do
30 | meta_ [charset_ "UTF-8"]
31 | title_ "My Page"
32 | link_ [rel_ "stylesheet", href_ bootstrapCss]
33 |
34 | body_ $
35 | div_ [class_ "jumbotron"] $
36 | h1_ ("Hello, " <> toHtml who <> "!")
37 | -- end snippet lucid-template
38 | where
39 | bootstrapCss = "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css"
40 |
41 | lucidHandler :: ScottyM ()
42 | lucidHandler =
43 | -- start snippet lucid-handler
44 | get "/greet-with-lucid/:who" $ do
45 | who <- param "who"
46 | html (renderText (homeView who))
47 | -- end snippet lucid-handler
48 |
49 | -- start snippet addNewComment
50 | type ArticleId = Text
51 |
52 | addNewComment :: ArticleId -> Text -> IO ()
53 | --- end snippet addNewComment
54 | addNewComment _ _ = return () -- only for slides
55 |
56 | articleHandler :: ScottyM ()
57 | articleHandler =
58 | -- start snippet article-handler
59 | post "/articles/:article-id/comments" $ do
60 | articleId <- param "article-id"
61 | -- accepts a form or query parameter "comment"
62 | comment <- param "comment"
63 | liftIO (addNewComment articleId comment)
64 | redirect ("/articles/" <> articleId)
65 | -- end snippet article-handler
66 |
67 | -- start snippet main
68 | main :: IO ()
69 | main = scotty 8080 app
70 | -- end snippet main
71 |
72 | messyHtml :: ScottyM ()
73 | messyHtml = do
74 | let bootstrapCss = "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css"
75 |
76 | get "/greet-with-template/:who" $ do
77 | who <- param "who"
78 | html $
79 | "\
80 | \\
81 | \\
82 | \ \
83 | \ My Page\
84 | \ bootstrapCss <> "\">\
86 | \\
87 | \\
88 | \ \
89 | \
Hello, " <> who <> "!
\
90 | \ \
91 | \\
92 | \\
93 | \"
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/listings/yesod-demo/templates/default-layout-wrapper.hamlet:
--------------------------------------------------------------------------------
1 | $newline never
2 | \
3 | \
4 | \
5 | \
6 | \
7 |
8 |
9 |
10 |
11 | #{pageTitle pc}
12 |
13 |
14 |
15 |
16 |
17 | ^{pageHead pc}
18 |
19 | \
22 |