building a website in haskell coming from node.js
TRANSCRIPT
I'm a web developer
I work at Busbudon our website
we are a JavaScriptshop (Node.js, React)
interested in
FunctionalProgramming
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 experiencebuilding something in Haskell
Hands-on warmupseanhess.github.io/2015/08/04/practical-haskell-getting-started.html
Course and exerciseswww.seas.upenn.edu/~cis194/spring13
Bookhaskellbook.com
Librarieshaskelliseasy.com
Best practicesdev.stephendiehl.com/hask
Getting started
choosing a web framework
Yesodbattle-testedgreat docsbig framework
Scottylightweightfamiliarnot a lot of docs
Servantleverages typesmost differentfairly new
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) {
// ...
};
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
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 decodingchallenge 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
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’
Docker & Docker Composedocs.docker.com/compose/install
Heroku Toolbelt & Heroku Docker Plugindevcenter.heroku.com/articles/docker
Dockerfilehub.docker.com/r/thoughtbot/heroku-haskell-stack
Heroku Docker & Stack
$ heroku docker:release
much syntax
haskellforall.com/2015/09/how-to-make-your-haskell-code-more.html
. $ !! @ <$> <*> <- >> >>= & ^. ^.. %~ .~ =~ ¯\_(ツ)_/¯
implicit versus
explicit and qualified importsimport 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 conflictsimport 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/bookgithub.com/Gabriel439/Haskell-Pipes-Librarygithub.com/Gabriel439/Haskell-Turtle-Library...
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