1

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.

TrebledJ
  • 8,713
  • 7
  • 26
  • 48
user3680029
  • 179
  • 8
  • 1
    Please include specific compiler errors, copied exactly. Also, is this your exact actual source? `getRequest` should fail to compile, because `liftIO parseRequest "http://......"` passes too many arguments to `liftIO`, but you seem to be writing as though that function were correct/complete. – amalloy May 30 '19 at 18:44
  • In my actual code lucid is undefined ``` lucid= undefined ``` Everything else compile correctly. getRequest compile don't read here my definition literally I skip the part responsible for building the request the last call of this function is a IO-lifted result from putStrLn (i.e IO () ) – user3680029 May 30 '19 at 19:22
  • I just fixed your tags. I guess you don't want _spock_ because it refers to a test framework written in Groovy. I replaed it by _haskell-spock_ which hopefully refers to the web development framework you are talking about. – kriegaex May 31 '19 at 04:07

1 Answers1

1

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)
Jeremy List
  • 1,756
  • 9
  • 16
  • html is a "converter" from Text to ActionCtxt ctx m a which is in fact wrapped somewhere underneath the SpockAction type ( SpockAction is a SpockActionCTxt wrapper which himself is a wrapper for a ActionCTxt type). I think that if I go the way you suggested Jeremy List I need to flatten out one layer of ActionCTxt . LiftIO is working fine the error I get is: Couldn't match type `ActionCtxT ctx0 m0 a0' with () Expected type: ActionCtxT () (WebStateM database session state) () Actual type: ActionCtxT () (WebStateM database session state) (ActionCtxT ctx0 m0 a0) – user3680029 May 31 '19 at 14:24
  • The remaining problem was your use of `return`; which was getting instantiated via the `instance Monad ((->) r)` declaration. – Jeremy List Jun 04 '19 at 00:07
  • 1
    perfect. I must have done something wrong previously what you propose in fact compile correctly. Thank you very much Jeremy. Nevertheless wandering with my errors so long was a very good exercise I finally get a deeper understanding of monad transformers. Still I'm not very satisified with the IO monad in my code, I feel there is another way. As I see everything come from the use of parseRequest which pull me into a monad (String -> m Request) - I made m to be a IO, and once you're in the IO monad you cannot get out... it's the "curse of the IO Monad" ("runIO" is the main function) – user3680029 Jun 04 '19 at 04:57