Praveen Singh Yadav
Praveen Singh Yadav

Reputation: 1861

perform IO inside wai application

I am following the basic dispatching section of wai application.

I am able to catch the url parameter. How can I perform IO operation using these params.

I would like to use runCommand of System.Process to execute a system command using these parameters.

:t runCommand give runCommand :: String -> IO ProcessHandle

my Main.hs

{-# LANGUAGE OverloadedStrings #-}

module Main where
import Control.Monad
import Network.Wai
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)
import Data.ByteString
import Control.Monad
import System.Process
import qualified Data.ByteString.Lazy.Char8 as L8

main :: IO ()
main = do
    run 8080 app

app :: Application
app request respond = respond $ case rawPathInfo request of
    "/" -> indexHtml 
    "/wake" -> wakeMeUP request
    _ -> fourNotFour

indexHtml :: Response
indexHtml = responseFile
    status200
    [("Content-Type","text/html")]
    "index.html"
    Nothing

wakeMeUP :: Request -> Response
wakeMeUP request = 
    let query = queryString request
        hour = join $ lookup "hour" query
        min = join $ lookup "min" query
    --I would like to use runCommand "using hour and min variables"
    in responseLBS
        status200
        [("Content-Type","text/plain")]
        "Alarm set at...to be coded later"

fourNotFour :: Response
fourNotFour = responseLBS

    status404
    [("Content-Type","text/plain")]
    "404 not found"

Upvotes: 1

Views: 383

Answers (1)

luqui
luqui

Reputation: 60513

Your design prevents it, because of how you have written app,

app request respond = respond $ case rawPathInfo request of

which says that you immediately respond. Note the type of Application:

type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived

Since the result type has an IO, you have the opportunity to do I/O before yielding a value. So you could:

app request respond = do
    whateverResult <- runCommand "whatever"
    respond $ ...

(You could also do it afterward, at least according to the types:

app request respond = do
    rcvd <- respond $ ...
    runCommand "whatever"
    return rcvd

Though that's a bit odd to do for the continuation-passing idiom being used here (the (a -> b) -> b pattern in the Application type). It means that the command will be run after everything else, for some definition of "everything else" that we can't know without reading the wai source.)

Anyway, you probably don't want the command to be run inside app, but rather in wakeMeUp, which means you need to change some types around. In particular,

wakeMeUp :: Request -> IO Response
--                     ^^

and suitably monadify the function. Then your app needs to not call respond immediately, so you can say

app request respond =
   response <- case rawPathInfo request of
       "/" -> return indexHtml
--            ^^^^^^
       "/wake" -> wakeMeUp request
--       no change, because wakeMeUp now has an IO return type
       ...
   respond response

If this is gibberish to you, it's time to do some monad tutorials. Or if you just want to get the damn thing working, I recommend Dan Piponi's The IO Monad For People who Simply Don't Care. Happy hacking!

Upvotes: 1

Related Questions