var express = require('express')
var app = express()
var store = undefined
app.post("/", function(req, res){
store = req.body
})
app.get("/", function(req, res){
res.send(store)
}
app.listen(some_port_num)
This is a simple nodejs/express server application which stores the http request body in a global variable and sends the same variable as responsse on a get request.
How to write this code in yesod/haskell. I don't know how to use global/module-scoped variables in haskell/yesod. And, is there any other way to share variables between two functions in haskell?
Using global constants is trivial in Haskell: you just write foo = bla
somewhere (preferrably with a signature foo :: FooType
). foo
can then be used anywhere in your module (or even outside, if you export it).
Global variables OTOH aren't really possible in Haskell, as such. For the most part, that's a good thing because global state tends to lead to lots of bugs. Often when programmers think about global variables, it turns out global constants would be just fine, or otherwise the mutable state should better be re-written as explicit parameters.
Sometimes however you pretty much do need state variables. In Yesod in particular, there's a place where you can store them sort-of globally: the Yesod. That is the data structure that your web application has as its “basement”. For instance, you may have
data YourYesod = YourYesod {
...
, reqStore :: IORef RequestBody -- IORef basically means, it's variable
-- in the imperative sense.
...
}
mkYesod "YourYesod" [parseRoutes| ... |]
The Yesod can be accessed from anywhere in the Handler
etc. monads, e.g. in
getHomeR :: Handler Html
getHomeR = do
...
yourYesod <- getYesod -- gain "access" to the Yesod
storeS <- liftIO $ readIORef (reqStore yourYesod) -- look up the variable state
...
liftIO $ writeIORef (reqStore yourYesod) newStoreS -- write new state
...
At the beginning of the program, the reqStore
will need to be initialised, by something like
main :: IO ()
main = do
...
initReqStore = newIORef emptyRequest
...
warp 3000 $ YourYesod ... initReqStore ...
Before doing it this way, think about whether the store
variable really needs to be that global. The Yesod is pretty much the most global scope you have for stuff like that; that means also danger of those typical bugs like in procedural languages. If the variable is only used within one handler, you might as well just introduce it locally, like
do
...
store <- newIORef emptyRequest
appPost "/" $ \req res -> do
liftIO $ writeIORef store $ body req
appGet "/" $ \req res -> do
storedReq <- liftIO $ readIORef store
send res storedReq