Andriy Drozdyuk
Andriy Drozdyuk

Reputation: 61091

What do this do in Snap?

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")
         ]
  1. Why is it returning ()?
  2. What is the standard signature of the "controller" (as you would call it in MVC speak) function?
  3. How does it know to use index.tpl as a template for / route?
  4. How do I get rid of authentication layer, say if I only want to make a simple personal web-app?

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

Answers (1)

Enra
Enra

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

Related Questions