I am beginner, so please bear with me.
I have a following code:
{-# LANGUAGE OverloadedStrings #-}
module Lib where
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Data.Monoid ((<>))
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import Test.WebDriver
--import Web.Scotty
import Web.Scotty.Trans
firefoxConfig :: WDConfig
firefoxConfig = defaultConfig
startMyBrowser :: WD a -> IO a
startMyBrowser = runSession firefoxConfig
stopMyBrowser = closeSession
someFunc :: WD String
someFunc = do
openPage "http://maslo.cz"
captionElem <- findElem (ByCSS "h2")
text <- getText captionElem
return $ T.unpack text
helloAction :: ActionT TL.Text WD ()
helloAction = do
a <- lift someFunc
text $ "got this for you: " <> TL.pack a
routes :: ScottyT TL.Text WD ()
routes = get "/hello" helloAction
startServer = startMyBrowser $ do
lift $ scottyT 3000 _ routes
stopMyBrowser
I am not sure if even those filled parts are right - it is supposed to start a Selenium session (startMyBrowser
), spin up a web server (the scottyT
part) and after web server stops it should end the Selenium session (stopMyBrowser
).
After fiddling with types I got to that code above and it seems that I only miss one piece - the hole.
Please, if you make it working, try to explain your solution and/or add some links to more materials. I would love to understand those damned transformers.
Edit 1: Here are the errors:
• Couldn't match type ‘t0 m0’ with ‘WD’
Expected type: WD ()
Actual type: t0 m0 ()
• In a stmt of a 'do' block: lift $ scottyT 3000 _ routes
In the second argument of ‘($)’, namely
‘do { lift $ scottyT 3000 _ routes;
stopMyBrowser }’
In the expression:
startMyBrowser
$ do { lift $ scottyT 3000 _ routes;
stopMyBrowser }
• Found hole:
_ :: WD wai-3.2.1.1:Network.Wai.Internal.Response
-> IO wai-3.2.1.1:Network.Wai.Internal.Response
• In the second argument of ‘scottyT’, namely ‘_’
In the second argument of ‘($)’, namely ‘scottyT 3000 _ routes’
In a stmt of a 'do' block: lift $ scottyT 3000 _ routes
• Relevant bindings include
startServer :: IO () (bound at src/Lib.hs:37:1)
Kind guy ForTheFunctionGod on reddit answered it, here it is:
There's your problem:
lift $ scottyT 3000 _ routes
Since the return type of scottyT
is
MonadIO n => n
You don't need to lift it - it can fit into any monad that can perform IO actions. WD
is such a monad - note its MonadIO
instance. You'd only need to lift scottyT
if its return type would be a simple IO
.
Type classes like MonadIO
, MonadState
, etc. mostly obviate the need to manually lift computations. Whereas you'd need to lift a function to type IO Int
if you wanted to embed it into StateT s IO Int
, you wouldn't need to lift a function of type MonadIO m => m a
, because StateT s IO
already is an instance of MonadIO
, so m
can be instantiated to it.
As for the hole: it has to be of type
WD Response -> IO Response
The only way to make this work is to use runSession
or one of its friends. I don't know Selenium that well, but, presumably, you can re-use your already opened session's id:
runWD :: WDSession -> WD a -> IO a
startServer = startMyBrowser $ do
sessionID <- getSession
scottyT 3000 (runWD sessionID) routes
stopMyBrowser
I haven't tried this out, but the types should check out. I hope it helps!
It definitely did :).