amit levy (@aalevy)
April 11, 2014 @ Heroku
$ whoami
This was done informally on my laptop, but more robust benchmarks have similar results
cabal
package managermain = putStrLn "hello world"
main = putStrLn "hello world"
Let's get a bit fancier:
main = do
putStr "What's your name? "
name <- getLine
putStrLn $ "Hello " ++ name ++ "!"
do
block returns a value (consumed with <-
) and can perform a side effect (e.g. print to the screen).do
block let's us write statements that have side effects.main
is the root of all side effects in a programBool
, Char
, Int
, Double
...
let amMutable = False
type1 ->
type2 - a function from type1 to type2. For example:
add :: Int -> Int -> Int
map :: (a -> b) -> [a] -> [b]
(
type1,
type2,
...,
typeN)
- a tuple()
- a zero-tuple, pronounced unit
void
in C()
data
or newtype
data HTMLString = UnescapedHTML String | EscapedHTML String
data Maybe a = Just a | Nothing
data Either a b = Left a | Right b
data BlogPost = BlogPost { postTitle :: String, postBody :: String }
newtype Password = Password { passwordDigest :: String }
type
aliasestype Point = (Double, Double)
main = run 3000 $ do
settings <- newAppSettings
controllerApp settings $ do
get "/" $ render "welcome.html" ()
routeName "/posts" $ do
get "/" $ do
posts <- findAllPosts
render "posts/index.html" posts
get "/:post_id" $ do
postId <- readQueryParam' "post_id"
routePost postId $ \post ->
render "posts/show.html" post
get "/:tag" $ do
tag <- queryParam' "tag"
posts <- findPostsByTag tag
render "posts/index.html" posts
routePost :: DBRef Post -> (Post -> Controller BlogSettings ())
-> Controller BlogSettings ()
routePost postId act = withConnection $ \conn -> do
mpost <- liftIO $ findRow conn postId
maybe (return ()) act mpost
type ControllerState s = (Request, s)
newtype Controller s a = Controller {
runController :: ControllerState s
-> IO (Either Response a, ControllerState s)
}
Request
and an app specific stateRequest
or app state might be modifiedIO
in the return type means we can perform IO
side-effectsGiven a Response
, construct a Controller
action that responds with it:
respond :: Response -> Controller s ()
respond resp = Controller $ \s -> return (Left resp, s)
Given a value (of arbitrary type a
), return it rather than responding:
return :: a -> Controller s a
return val = Controller $ \s -> return (Right val, s)
Get the Request
value:
request :: Controller s Request
request = Controller $ \s -> return (Right $ fst s, s)
These are provided by Simple but don't do anything special. Just used everywhere.
Remember routeName
from the example?
...
routeName "posts" $ do
...
...
If the first thing in the Request
path ==
the name, match this route, otherwise try the next action:
routeName :: Text -> Controller s () -> Controller s ()
routeName name (Controller next) = do
req <- request
if (length $ pathInfo req) > 0 && name == (head $ pathInfo req)
then Controller $ \(req, s) -> next (popHdr req, s)
else return ()
where popHdr req = req { pathInfo = (tail . pathInfo $ req) }
routeHost
, routeMethod
, routeAccept
...routePost
).Let's write a blog...
$ cabal install simple
$ smpl create test_app
$ cd test_app
$ cabal install --only-dependencies
$ cabal run