-
Notifications
You must be signed in to change notification settings - Fork 374
ghci
Use stack ghci
to start a GHCi session:
stack ghci
The Application
module of the scaffolding provides the db
and handler
functions to run database requests and Handler
actions from the REPL:
ghci > import Application
ghci > db $ selectList [UserName ==. "foo"] []
ghci > import Application
ghci > renderingFunction <- Application.handler $ getUrlRender
ghci > renderingFunction HomeR
"http://localhost:3000/"
Note that many Handler
actions only make sense in the context of an actual HTTP request, and they may fail when from GHCi.
ghci > handler $ notModified
*** Exception: runFakeHandler issue: InternalError "runFakeHandler: no result"
Assuming you wanted to debug the following code, which you've added to Model.hs
:
-- * Model.hs lines 13-17
foo :: [Integer] -> [Integer]
foo xs =
let ys = map (+1) xs
zs = map (+2) xs
in ys ++ zs
Start a GHCi session with stack ghci
:
stack ghci
Set the module to be interpreted:
ghci > :add *Model # The asterisk makes it interpreted
Add your breakpoint:
ghci > :break Model 17
Breakpoint 0 activated at Model.hs:17:8-15
Run the foo
function to stop at your breakpoint and evaluate intermediate variables:
ghci > foo [1,2,3]
Stopped at Model.hs:17:8-15
_result :: [Integer] = _
ys :: [Integer] = _
zs :: [Integer] = _
ghci > ys
[2,3,4]
ghci > zs
[3,4,5]
You can also hit breakpoints from a URL request from your browser:
ghci > :add *Handler.Home
ghci > :break Handler.Home 20
ghci > Application.appMain -- Start the server
Now hit http://localhost:3000 from your browser.
GET /
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Stopped at Handler/Home.hs:(20,5)-(23,32)
_result :: HandlerT App IO Html = _
formEnctype :: Enctype = _
formWidget :: Widget = _
handlerName :: Text = _
submission :: Maybe (FileInfo, Text) = _
ghci > handlerName
"getHomeR"
ghci (which you should use via cabal repl
) can be used to inspect and run your Yesod app.
The latest scaffold provides a file app/DevelMain.hs that you can run your Yesod app with and achieve fast reloads. That file has instructions at the top. This page has some advance info.
Restarting ghci does not kill running threads. One technique to handle this is to register all your threads so you have an easy target for shutting them down.
forkHandler :: (SomeException -> Handler ()) -> Handler () -> Handler ()
-- This is used to avoid accumulating threads in ghci
forkHandler exHandle handler | not development = Yesod.forkHandler exHandle handler
forkHandler exHandle handler | otherwise = do
app <- getYesod
let updateTids = atomicModifyIORef' (activeThreadIds app)
Yesod.forkHandler exHandle $ do
tid <- myThreadId
liftIO $ updateTids $ \tids -> (insertSet tid tids, ())
finally handler $
liftIO $ updateTids $ \tids -> (deleteSet tid tids, ())
shutdownApp :: App -> IO ()
shutdownApp app = readIORef (activeThreadIds app) >>= mapM_ killThread
This needs to be integrated into DevelMain.hs. One aspect is that you need access to the App, so you can add this function to Application.hs
getApplicationRepl :: IO (Int, App, Application)
getApplicationRepl = do
conf <- getDevelConf
(app1, site) <- makeApplicationFoundation conf
(p, app2) <- defaultDevelApp (return conf) (const (return app1))
return (p, site, app2)
Then in DevelMain.hs add shutdownApp as a finalizer
start done = do
(port, site, app) <- getApplicationRepl
forkIO (finally (runSettings (setPort port defaultSettings) app)
(putMVar done () >> shutdownApp site))