问题描述
大家好,感谢您抽出宝贵时间。
我有一个错误,我不确定错误是什么,但我认为问题是:
没有从 ext-1.2.4.1:Data.Text.Internal.Lazy.Text IO)
到 Web.Scotty.Internal.Types.ScottyT
的 IO 转换器。
但我想知道为什么编译器可以使用 ext-1.2.4.1:Data.Text.Internal.Lazy.Text IO)
。这就是为什么我只使用 String 并删除了所有出现的 {-# LANGUAGE OverloadedStrings #-}
但仍然出现错误的原因。另一方面,这应该是 IO [String]
,不是吗?
正如您所提到的,我真的不知道 ext-1.2.4.1:Data.Text.Internal.Lazy.Text IO)
是什么。
在另一个地方,我已经成功地将 liftIO
用于 a -> IO String
函数。而且我认为我以同样的方式使用它们。
我想我会慢慢了解 monad 是什么,但不太确定。 我真的不知道为什么我必须使用 lift
函数。
错误信息:
• No instance for (Monadio
(Web.Scotty.Internal.Types.ScottyT
text-1.2.4.1:Data.Text.Internal.Lazy.Text IO))
arising from a use of ‘liftIO’
• In a stmt of a 'do' block:
paths <- liftIO $ getAllFilePaths2 path
In the expression:
do paths <- liftIO $ getAllFilePaths2 path
pathsToScotty paths
In an equation for ‘pathsToScotty2’:
pathsToScotty2 path
= do paths <- liftIO $ getAllFilePaths2 path
pathsToScotty paths
|
49 | paths <- liftIO $ getAllFilePaths2 path
发生错误的地方:
import Control.Monad.IO.Class
...
pathsToScotty2 :: String -> ScottyM ()
pathsToScotty2 path = do
paths <- liftIO $ getAllFilePaths2 path
pathsToScotty paths
getAllFilePaths2 :: String -> IO [String]
getAllFilePaths2 dir = do
putStrLn dir
isFile <- doesFileExist dir
if isFile
then return [dir]
else do
dirs <- listDirectory dir
foldl foldHelper2 (return []) $ map (\d -> show $ mconcat [dir,"/",d ]) dirs
foldHelper2 :: IO [String] -> String -> IO [String]
foldHelper2 ps path = do
paths <- ps
newPaths <- getAllFilePaths2 path
return (paths ++ newPaths)
解决方法
真正理解 monad 需要时间、练习和耐心,但通过检查您的类型来理解 liftIO
的必要性应该不会太难。
首先,liftIO
的类型是 MonadIO m => IO a -> m a
。这意味着该函数可以将任何 IO 操作转换为 monad m
中的操作,只要 m
具有 MonadIO
的实例。理论上,这只能在 m
有某种处理 IO
动作的方式时才能实现,因此该函数将给定的动作嵌入到 m
monad 中。
您绝对是在正确的地方使用 liftIO
,为什么它不起作用?也就是说,您有一个 getAllFilePaths2 path
类型的值 IO [String]
,并且您希望它是一个 ScottyM [String]
类型的值 - 这确实是使用 {{1} 的好地方}}。但是,liftIO
不是 ScottyM
的实例,因为您看到的错误消息试图告诉您,因此您不能使用 MonadIO
。
这可能看起来很疯狂——你真的不能将 IO 操作嵌入到 liftIO
中吗?——但实际上有一个很好的理由。如果 ScottyM
操作引发错误,会发生什么情况?您的整个网络应用程序是否崩溃?如果您天真地使用IO
,它会如此。相反,scotty 提供了函数 liftAndCatchIO
,正如文档描述的那样,它“像 liftIO
,但捕获任何 IO 异常并将它们转换为 Scotty 异常。”这是将 liftIO
操作嵌入 Scotty 的首选方式。
最后的问题来了:请注意,IO
实际上生成的是 liftAndCatchIO
类型的值,而不是 ActionM a
。此外,无法在 ScottyM a
monad 中获取值并将其放入 ActionM
monad。相反,您需要将该值用作操作。因此,我不确定 ScottyM
的作用,但您很可能需要重写它。