Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Post and Get a variable using yesod server

Tags:

haskell

yesod

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?


1 Answers

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
like image 152
leftaroundabout Avatar answered Jun 26 '26 07:06

leftaroundabout



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!