I'm guessing your code is using a call to mkYesod
. In order to make a split into multiple modules, you'll instead need to use mkYesodData
and mkYesodDispatch
as is done in the scaffolding site. For more information on this, please see the scaffolding chapter. It shouldn't be too difficult to adapt this technique to work with an existing codebase.
How do I put my routes in one module and my handlers in another module
Вопрос
I have a Yesod application in which the entire web portion of the application was defined in one file, and it has grown to the size that I need to separate things out.
I want to set up a hirearchy like this:
web/Handlers/Group1
Group2
web/Foundation
web/Main
This is similar to how the code for the Haskellers website is set up. However, I can't figure out how the Haskellers website actually works. When I set things up, my handlers in Group1 need to import Foundation to get the Foundation class and to get a list of other routes, because some of my handlers redirect to different parts of the app. But my Foundation
won't compile because it wants to find the route handlers that are defined in Group1
, Group2
, etc.
This is forcing me into a circular import, which obviously won't work. When I read the Haskellers code, the Foundation
module does not import any of the Handler
modules.
What is the trick to make this work?
Update
Following Michael Snoyman's answer, I've replaced my call to mkYesod
with separate calls to myYesodData
and mkYesodDispatch
. GHC complains, of course, if I do both in one file, so I separated stuff out in the first stage of a refactoring:
- All of my code from
Main.hs
, which was all of the code of the web portion of the application, I moved toWebApp.hs
- I moved the
main
function fromWebApp.hs
back intoMain.hs
- In
WebApp.hs
I callmkYesodData
- In
Main.hs
I callmkYesodDispatch
And the linker fails now. When compiling Main
, the first module it finds in the import list that it is in this project and not in a library fails to link like so:
web/Main.hs:1:1:
cannot find normal object file `dist/build/invoicedb/invoicedb-tmp/WebApp.o'
while linking an interpreted expression
If I drop mkYesodDispatch
and mkYesodData
and return to mkYesod
, even using this same file structure, the build proceeds perfectly.
So, to review, here are my files:
web/Main.hs:
- The
main
function - The call to
mkYesodDispatch
config/routes:
- plain text routing, just like in the scaffold app
web/WebApp.hs:
- My
App
structure mkYesodData
- instance Yesod App
- instance YesodAuth App
- instance RenderMessage App FormMessage
- All of my routing handlers
I have created a trivial example that illustrates this issue: https://bitbucket.org/savannidgerinel/yesod-decomposition/src/
[2 of 3] Compiling Dispatch ( src/Dispatch.hs, dist/build/yesod-decomposition/yesod-decomposition-tmp/Dispatch.p_o )
src/Dispatch.hs:1:1:
cannot find normal object file `dist/build/yesod-decomposition/yesod-decomposition-tmp/Foundation.o'
while linking an interpreted expression
If you edit the code, commenting out mkYesodDispatch
in Dispatch.hs, and replacing mkYesodData
with mkYesod
in Foundation.hs, the code will compile successfully.
Note, I'm using yesod-1.2.4
for this. If 1.2.6 solves it, then I will get my team upgraded.
Решение
Другие советы
This looks like a bug in your .cabal
file. Make sure that you tell cabal that you are using Template Haskell:
executable yesod-decomposition
..
other-extensions: TemplateHaskell
So that it knows to build the right object files in the right order.