【发布时间】:2017-01-18 09:40:12
【问题描述】:
尝试制作一个 yesod 应用程序(没有堆栈和yesod init),当我编译它时,我遇到了这个 monad 错误。我知道这些问题很常见,但我从未见过有人用这些特定类型(即 HandlerT 和 IO 字符串)提出这个问题。这是我所有的代码,所以我认为有人测试它应该很容易。另外我使用的是 GHC 7.10.3,它有点旧,但我认为我的问题与编译器版本无关。
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.10.3
这是编译器错误消息:
$ ghc -o yesod_test yesod_monad_test.hs
[1 of 1] Compiling Main ( yesod_monad_test.hs, yesod_monad_test.o )
yesod_monad_test.hs:25:15:
Couldn't match expected type `HandlerT HelloWorld IO Text'
with actual type `IO String'
In a stmt of a 'do' block: snippet <- readFile temp
In the expression:
do { let temp = "posts/" ++ title ++ ".html";
snippet <- readFile temp;
defaultLayout
((asWidgetT . toWidget) (toHtml (preEscapedText snippet))) }
这是我的代码。
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
import Yesod
import Network.Wai (pathInfo, rawPathInfo, requestMethod, responseLBS)
import Data.Text (Text)
import Text.Blaze (preEscapedText)
import Control.Exception (IOException, try)
import Control.Monad (when)
data HelloWorld = HelloWorld
mkYesod "HelloWorld" [parseRoutes|
/post/#String PostR GET
|]
instance Yesod HelloWorld
getPostR :: String -> Handler Html
getPostR title = do
let temp = "posts/" ++ title ++ ".html"
snippet <- readFile temp
defaultLayout [whamlet|#{preEscapedText snippet}|]
main :: IO ()
main = warp 3000 HelloWorld
我很惊讶这段代码不起作用,因为我相信我非常密切地遵循教程 (http://www.yesodweb.com/book/routing-and-handlers#routing-and-handlers_overlap_checking),尽管我在 Text 类型上使用字符串。
谢谢。
【问题讨论】:
-
你正试图在一个 do 块内为一个不是 IO 单子 (
Handler Html) 的单子执行 IO (readFile)。我不知道Handler类型同义词的详细信息,但 IO 存在于其定义中的事实表明它将支持某些操作,例如liftIO,您可以使用它来将 IO 操作提升到适当的地点。 -
基本上,@amalloy 告诉您将
snippet <- readFile temp替换为snippet <- liftIO $ readFile temp。