How to stack correctly monads to finally render to html?

244 Views Asked by At

My goal is to feed correctly the html function (as define in Spock-core) with a HtmlT m type (ideally Html ()). In between I am performing some Network.Http.Simple requests. Apparently my knowledge of Haskell is insufficient, I didn't find the way to force the correct monad. As far as I know (and understand the whole sense of monads) there is no way to compose different monads something like (Monad M, Monad N => M a -> (a -> N b) -> N b).

The best I manage to achieve was to set up a HtmlT IO () type but then I get stuck in the conversion function lucid :: HtmlT IO () -> SpockAction dtb sess state ()

Here is my connection function (Auth is a FromJSON data structure to host authorization key and token)

     connect :: IO Auth
     connect = do  
                ...building a http request.. 
                response <- httpJSON request
                return (getResponseBody response :: Auth)

Next this get wired in a getRequest function of type String -> HtmlT IO ()

    getRequest :: RequestPath -> HtmlT IO ()
    getRequest rpath = do
                    atoken <- liftIO connect
                    request' <- liftIO parseRequest "http://......"
                    let request = { series of set methods
                                    to build the request }
                    response <- httpLBS request
                    liftIO (L8.putStrnLn $ (getResponseBody response))

and here we come to the lucid function, Lucid can handle transformation Html () -> SpockAction .... Html () is nothing else than HtmlT Identity () so my first attempt was to feed lucid with HtmlT IO ().

   lucid :: HtmlT IO () -> SpockAction database session state ()
   lucid document = do 
                        txt <- renderTextT document       --> inside IO (?)
                        return html (TL.toStrict txt)     <-- naive attempt to 
                                      return to "somewhere" of course stupid..

Maybe IO is not the good monad here? When I try the Identity monad (to have a HtmlT Identity ()) so if I define connect as connect :: Identity Auth then naturally it follows that I am asked for a FromJSON instance of Identity (arising from using httpJSON), maybe that's a potential solution once I am in the Identity monad I am able to wire things up and probably finish with a clean Html () type which will be then executed smoothly by my lucid function.

Thank you for any clue or hints, maybe my approach is totally wrong the whole thing I am working on is to query a restAPI website and view the result on a web server run with Spock.

1

There are 1 best solutions below

3
On

The relevant types:

document :: HtmlT IO ()
renderTextT :: Monad m => HtmlT m a -> m Text
renderTextT document :: IO Text
<the value you need> :: SpockAction database session state Text

Fortunately we have this available: liftIO :: MonadIO m => IO a -> m a

Two steps to getting your lucid function running:

  1. Change txt <- renderTextT document to txt <- liftIO (renderTextT document)
  2. Try to compile your code to make sure liftIO is imported (it probably is already). If not: add import Control.Monad.IO.Class to your import list.
  3. Remove the return from return html (TL.toStrict txt).

The following code is working on my machine:

lucid :: HtmlT IO () -> SpockAction database session state ()
lucid document = do
  txt <- liftIO (renderTextT document)
  html (TL.toStrict txt)