Reputation: 61091
Could someone explain what is going on in the default snap project template?
--------------------------------------------------------------------------
-- | Handle login submit
handleLoginSubmit :: Handler App (AuthManager App) ()
handleLoginSubmit =
loginUser "login" "password" Nothing
(\_ -> handleLogin err) (redirect "/")
where
err = Just "Unknown user or password"
-- | Render login form
handleLogin :: Maybe T.Text -> Handler App (AuthManager App) ()
handleLogin authError = heistLocal (bindSplices errs) $ render "login"
where
errs = [("loginError", textSplice c) | c <- maybeToList authError]
| The application's routes.
routes :: [(ByteString, Handler App App ())]
routes = [ ("/login", with auth handleLoginSubmit)
, ("/logout", with auth handleLogout)
, ("/new_user", with auth handleNewUser)
, ("", serveDirectory "static")
]
index.tpl
as a template for /
route?The documentation and a tutorial mainly covers the snapplets (or the templates), but it does not go over any of the Haskell. It would be nice to see an example of how to write a book-store like app, or a blog (the official snap website stores their blog entries in markdown - so I am not sure what's going on there).
Upvotes: 1
Views: 363
Reputation: 86
I'm in the process of learning Haskell myself, and I know nothing about Snap, but I can do my best to answer what I can see:
1)
The Snap.Snaplet
module defines a type: Handler b v a
.
So, any Handler
has three type parameters: b
, v
, and a
.
Also, (Handler b v)
is declared in the same module to be a Monad
. That can probably tell you something about what the last a
parameter is for.
IO
is another example of a Monad
.
IO ()
does "something" that has to do with IO, and then returns ()
, an empty value.
IO a
does "something" that has to do with IO, and then returns something else of type a
.
For example, it doesn't really make sense to do x <- putStrLn "text"
because putStrLn has return type IO ()
. Technically, you can, but it's usually not useful. putStrLn
does IO and that's all, declining to pass anything onward to future functions. Specifically, it prints something to the console, but it doesn't tell the rest of the program that it did so.
str <- getLine
makes sense because getLine
has type IO String
. It does IO, and then tells str
about a String
. It produces a result that the rest of the function can use directly.
For Handler
, you could forget what a handler does and look at it like this:
let M = Handler b v
M
is declared to be a monad.
So, M a
does "something" and then returns an a
.
M ()
does "something" and doesn't return anything.
From that you can probably tell something about what functions like handleLogin
are doing. It takes its arguments and probably does something to/with the Handler
based on those. Afterwards, assuming there were no errors, the program moves to the next line without telling the function on the next line what happened.
There are some functions in Snap that return a Handler
with something other than ()
for the last parameter. If you see a function like that, it means it carries a meaningful return value.
4)
I suspect the templates are more of examples than things you are meant to build from, but it's all manually written, so I'd think you could remove the authentication steps if you like. The type App
is defined in "Application.hs" where you could remove its auth
parameter. Then you could remove references to it in the rest of the project.
Upvotes: 5