Yesod 1.6 was released in February of this year. This new version changes how subsites are implemented, which breaks most Yesod web applications. I did not find many good resources describing how to migrate web applications to 1.6, so I am documenting the steps I took to fix my website. It was not immediately obvious what changes needed to be made, so hopefully this is helpful for others.
My website was originally running on Stackage LTS 9.2, so I upgraded to LTS 12.2 by updating my
I recompiled the website with
stack build, but this gave me version conflicts for dependencies. I fixed the version conflicts for dependencies in my
<package>.cabal file by widening the accepted versions in the
build-depends section (I actually just got rid of most of the version constraints). If you use hpack, you'll need to edit the dependencies in your
After fixing the version conflicts, I started getting compiler errors:
src/Foundation.hs:94:5: error: ‘shouldLog’ is not a (visible) method of class ‘Yesod’ | 94 | shouldLog app _source level = | ^^^^^^^^^
It looks like
shouldLog has been removed. I just commented it out in
Then I got the following errors:
src/Foundation.hs:147:9: error: • Couldn't match kind ‘* -> *’ with ‘*’ When matching types m0 :: * -> * site0 :: * Expected type: YesodDB site0 (AuthenticationResult App) Actual type: ReaderT (YesodPersistBackend site0) (t0 m0) (AuthenticationResult App) • In a stmt of a 'do' block: x <- getBy $ UniqueUser $ credsIdent creds In the second argument of ‘($)’, namely ‘do x <- getBy $ UniqueUser $ credsIdent creds case x of Just (Entity uid _) -> return $ Authenticated uid Nothing -> do ...’ In the expression: runDB $ do x <- getBy $ UniqueUser $ credsIdent creds case x of Just (Entity uid _) -> return $ Authenticated uid Nothing -> do ... | 147 | x <- getBy $ UniqueUser $ credsIdent creds | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It looks like the authentication API has changed. This makes sense since authentication is implemented in a subsite, and we know subsites have been updated. To fix this, I call
liftHandler outside of
runDB to run database operations in the site's root monad:
authenticate creds = liftHandler $ runDB $ do
I also run an IO computation inside the database computation, so I eliminated one lift:
-- Original now <- lift $ lift getCurrentTime -- Fixed now <- lift getCurrentTime
When attempting to compile again, I received the following error:
src/Foundation.hs:160:23: error: • Could not deduce: m ~ (->) a0 from the context: (MonadHandler m, HandlerSite m ~ App) bound by the type signature for: authHttpManager :: forall (m :: * -> *). (MonadHandler m, HandlerSite m ~ App) => m Manager at src/Foundation.hs:160:5-19 ‘m’ is a rigid type variable bound by the type signature for: authHttpManager :: forall (m :: * -> *). (MonadHandler m, HandlerSite m ~ App) => m Manager at src/Foundation.hs:160:5-19 Expected type: m Manager Actual type: a0 -> Manager • In the expression: getHttpManager In an equation for ‘authHttpManager’: authHttpManager = getHttpManager In the instance declaration for ‘YesodAuth App’ • Relevant bindings include authHttpManager :: m Manager (bound at src/Foundation.hs:160:5) | 160 | authHttpManager = getHttpManager | ^^^^^^^^^^^^^^
It looks like some part of the HTTP manager API changed.
authHttpManager has a default implementation though, so I just commented it out. You may need to implement this if you are not using the default global manager.
The following is the last error I encountered:
app/DevelMain.hs:40:1: error: Could not find module ‘Foreign.Store’ Perhaps you meant Foreign.Ptr (from base-126.96.36.199) Foreign.Safe (from base-188.8.131.52) Foreign.Storable (from base-184.108.40.206) Use -v to see a list of the files searched for. | 40 | import Foreign.Store | ^^^^^^^^^^^^^^^^^^^^
Foreign.Store is in a separate package now, so I added its package,
foreign-store to my cabal file.
Once I knew what to do, upgrading was relatively painless. If you would like to learn more about the changes in Yesod 1.6, check out Michael Snoyman's blog post.