Building a website
in Haskell
coming
from Node.js
@nicolas_hery
I'm a web developer
I work at Busbud
on our website
we are a JavaScript
shop (Node.js, React)
interested in
Functional
Programming
make API/DB calls, put data in HTML, respond
to HTTP requests
work on a team, modify other people's code
constantly adapt to business change
a web developer in a startup needs to...
Was it hard challenging? Yes
Was it possible? Yes
Would I do it again? Yes!
my experience
building something in Haskell
The app
developer.marvel.com
port existing JavaScript app to Haskell
Jumping in
Hands-on warmup
seanhess.github.io/2015/08/04/practical-haskell-getting-started.html
Course and exercises
www.seas.upenn.edu/~cis194/spring13
Book
haskellbook.com
Libraries
haskelliseasy.com
Best practices
dev.stephendiehl.com/hask
Getting started
choosing a web framework
Yesod
battle-tested
great docs
big framework
Scotty
lightweight
familiar
not a lot of docs
Servant
leverages types
most different
fairly new
Code examples
types
document your
domain data
function renderCharacterDetails(character) {
// what is the shape of `character`?
};
data Character = Character
{ id :: CharacterId
, name :: Text
, description :: Maybe Text
, urls :: [Url]
, thumbnail :: Image
, comics :: ComicList
}
renderCharacterDetails :: Character -> Html
renderCharacterDetails character = -- ...
var t = require('tcomb');
var Character = t.struct({
id: t.Number,
name: t.String,
description: t.maybe(t.String),
urls: t.list(Url),
thumbnail: Image,
comics: ComicList
}, 'Character');
function renderCharacterDetails(character) {
// ...
};
Maybe/Either
force you to
handle possible cases
(missing data, errors)
Character.getMarvelUrl = function(character) {
// will this always return a string?
return character.urls.find(function(url) {
return url.type === 'detail';
}).url;
};
getMarvelUrl :: Character -> Text
getMarvelUrl character =
let defaultUrl = "http://marvel.com"
maybeUrl = find isDetailUrl (urls character)
in case maybeUrl of
Nothing -> defaultUrl
Just detailUrl -> U._url detailUrl
some things that
are trivial in JavaScript
are hard take longer in Haskell
(ex: IO)
function getCharactersController(req, res, next) {
var offset = req.query.offset;
// ...
}
GET /characters?offset=20
(offset is optional)
getCharactersController :: HandlerM ()
getCharactersController = do
offset :: Int <- param "offset"
`rescue` (_ -> return 0)
-- ...
explicit JSON decoding
challenge when working with more
complex data structures
artyom.me/aeson
{
"code": 200,
"status": "Ok",
"data": {
"total": 1,
"count": 1,
"results": [
{
"id": 1009610,
"name": "Spider-Man",
// ...
}
]
}
}
GET /api/characters/1009610
var body = JSON.parse(response);
var character = Character(body.data.results[0]);
return {
character: character
};
data CharacterResponse = CharacterResponse
{ character :: Character }
instance FromJSON CharacterResponse where
parseJSON (Object o) = do
_data <- o .: "data"
_results :: [Character] <- _data .: "results"
case _results of
[] -> fail "data.results should be non-empty"
(x:_) -> return CharacterResponse { character=x }
parseJSON _ = mzero
refactoring
is easy
data Comic = Comic
{ id :: ComicId
- , title :: Text
+ , name :: Text
, description :: Maybe Text
, urls :: [Url]
$ stack build
Views/Components/ComicsList.hs:23:57:
Not in scope: ‘C.title’
Views/Components/ComicDetails.hs:29:31:
Not in scope: ‘C.title’
Views/Components/ComicDetails.hs:30:33:
Not in scope: ‘C.title’
Views/Pages/Comic.hs:27:50:
Not in scope: ‘C.title’
Controllers/Comic.hs:71:23:
Not in scope: ‘C.title’
Shipping
Docker & Docker Compose
docs.docker.com/compose/install
Heroku Toolbelt & Heroku Docker Plugin
devcenter.heroku.com/articles/docker
Dockerfile
hub.docker.com/r/thoughtbot/heroku-haskell-stack
Heroku Docker & Stack
$ heroku docker:release
observations
the Good
if it compiles
it works
optimize
for change
type-safe URLs
type-safe i18n
type-safe JS hooks
focus on logical errors
versus malformed code
influence the way you
write JavaScript
observations
the Bad & the Ugly
much syntax
haskellforall.com/2015/09/how-to-make-your-haskell-code-more.html
. $ !! @ <$> <*> <- >> >>= & ^. ^.. %~ .~ =~ ¯_(ツ)_/¯
implicit versus
explicit and qualified imports
import Data.Text.Lazy
import Network.HTTP.Types
import Text.Blaze.Html.Renderer.Text
import Web.Scotty.Trans
import qualified Data.Text.Lazy as TL
import Network.HTTP.Types (status404, status500)
import Text.Blaze.Html.Renderer.Text (renderHtml)
import Web.Scotty.Trans (ActionT, html, param, status)
record labels can cause
naming conflicts
import qualified Models.Character as C
import qualified Models.ComicSummary as CS
characterDetailsView :: Character -> Html
characterDetailsView character =
-- ...
H.img ! A.alt (toValue (C.name character))
-- ...
H.li $ toHtml (CS.name comicSummary)
danger of too much abstraction
www.yesodweb.com/blog/2015/10/beginner-friendly-code-and-apis
someFunc =
maybe getDefaultValue otherFunc . flip lookup someMap
someFunc key =
case lookup key someMap of
Nothing -> getDefaultValue
Just value -> otherFunc value
documentation
often lack examples
(type signatures not enough for beginners)
exceptions:
www.yesodweb.com/book
github.com/Gabriel439/Haskell-Pipes-Library
github.com/Gabriel439/Haskell-Turtle-Library
...
Wrap up
google "startup blog haskell"
www.wagonhq.com
www.frontrowed.com
...
are people actually doing this?
github.com/pbrisbin/tee-io
github.com/thoughtbot/carnival
github.com/liqd/thentos
open-source
example apps are really helpful
pinboard.in/u:nicolashery/t:haskell/t:example
some
closing thoughts
learn the basics and then build something
don't try to learn everything at once
don't be afraid to ask for help
Thank you.
github.com/nicolashery/example-marvel-haskell

Building a website in Haskell coming from Node.js