将yesod app源码拆分成多个源文件

Split yesod app source code into multiple source files

这确实是一个新手问题,但我在网上或 Whosebug 上找不到任何相关信息。可能是我搜索错了..

我有一个 yesod 应用程序,其中所有内容都在一个文件中,但我不知道如何将函数移动到单独的文件中。这是一个最小的例子:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}

module Main where

import Yesod

data App = App
mkYesod "App" [parseRoutes|
/    HomeR    GET
|]
instance Yesod App

getHomeR = defaultLayout $ toWidget [hamlet|Hello Whosebug|]

main = warp 8080 App

如何将 getHomeR 函数移动到一个单独的文件中?在 getHomeR 中,我需要访问 App,但 mkYesod "App" 需要访问 getHomeR。这看起来像一个循环依赖。但不知何故,必须有可能创建由多个源文件组成的 yesod 应用程序。

可以做的是将独立于App的功能移动到单独的文件中。但是当 App 增长并包含越来越多的功能时,这就变得不方便了,因为所有顶级处理函数仍然需要在同一个文件中。而且我不想使用yesod模板,因为我不明白它在做什么。


解决评论中提出的解决方案:

You can define the functions in your separate files, and import these in the Main. This is more or less how it is done in the yesod-mysql stack template.

错误信息:"No instance for (Yesod site0) arising from a use of `defaultLayout'".
而当我在GetHomeR.hs中import Main时,错误信息就变成了"Ambiguous type variable site0' arising from a use ofdefaultLayout'".
当我添加 getHomeR :: Handler Html 时,它编译了一会儿,但是:现在我必须从 Main.hs 到 import GetHomeR 。 GHC 抱怨循环依赖的警告:

Module imports form a cycle:
         module `Main' (app\Main.hs)
        imports `GetHomeR' (app\GetHomeR.hs)
  which imports `Main' (app\Main.hs)

这感觉不对。这是正确的做法吗?

我终于自己弄明白了。

mkYesodData documentation 说:

Sometimes, you will want to declare your routes in one file and define your handlers elsewhere. For example, this is the only way to break up a monolithic file into smaller parts. Use this function, paired with mkYesodDispatch, to do just that.

您必须在 App 和 Yesod 实例所在的位置创建一个 Foundation 模块:

Foundation.hs:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}

module Foundation where

import Yesod

data App = App

mkYesodData "App" [parseRoutes|
/    HomeR    GET
|]

instance Yesod App

在你的Main.hs中,你调用路由:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}

module Main where

import Yesod
import Foundation
import GetHomeR (getHomeR)

mkYesodDispatch "App" resourcesApp

main = warp 8080 App

它又从 GetHomeR 模块调用 getHomeR。 GetHomeR.hs:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}

module GetHomeR (getHomeR) where

import Yesod
import Foundation

getHomeR :: Handler Html
getHomeR = defaultLayout $ toWidget [hamlet|Hello Whosebug|]

mkYesod 基本上类似于 mkYesodData + mkYesodDispatch,区别在于 mkYesodData + mkYesodDispatch 不能在同一个文件中,因为 "stage restriction"(错误消息:"GHC stage restriction: `resourcesApp' is used in a top-level splice, quasi-quote, or annotation, and must be imported, not defined locally")

所有路由处理程序(getHomeR,...)必须在调用 mkYesodDispatch(或 mkYesod)的位置可见。