0
{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative

import Data.Monoid
import Data.String

import Network.Wai.Middleware.RequestLogger

import Web.Scotty

data FullName = FullName { firstName :: String, lastName :: String }

lastFirst :: FullName -> String
lastFirst fn = lastName fn ++ ", " ++ firstName fn

main = scotty 3000 $ do
    middleware logStdoutDev

    get "/lastfirst/:fn/:ln" $ do
        fullName <- FullName <$> param "fn" <*> param "ln"
        html $ fromString (lastFirst fullName)

You may notice that the last part of the code use applicative function to construct a record, can someone explain why it shouldn't be created as usual?

nobody
  • 414
  • 2
  • 8
  • 18

1 Answers1

2

This is because param "fn" is not a String, but an ActionM which produces a String. We don't want to store the action inside FullName, we want to run the action, take its result, and store that.

We could run the actions as follows

get "/lastfirst/:fn/:ln" $ do
    fn <- param "fn"
    ln <- param "ln"
    let fullName = FullName fn ln
    html $ fromString (lastFirst fullName)

but the applicative syntax is easier to read.

chi
  • 111,837
  • 3
  • 133
  • 218
  • Thanks for your reply, can we construct a new record by FullName <$> "John" <*> "Hanks" ? Why? – nobody Dec 08 '15 at 13:40
  • @nobody No, just use `FullName "John" "Hanks"` since those are strings, not actions. Use `<$>/<*>` only when you have actions around, and you need to execute them to get their result. – chi Dec 08 '15 at 13:49
  • Thank you very much for your explanation! – nobody Dec 08 '15 at 14:33