Haskell Monads 和 liftIO 我不明白
Haskell Monads and the liftIO I don't get it
大家好,感谢您抽出宝贵时间。
我有一个错误,我不确定错误是什么,但我认为问题是:
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
,为什么它不起作用?也就是说,您有一个 IO [String]
类型的值 getAllFilePaths2 path
,并且您希望它是 ScottyM [String]
类型的值——这确实是使用 [=10] 的好地方=].但是,ScottyM
不是 MonadIO
的实例,因为您看到的错误消息试图告诉您,所以您不能使用 liftIO
.
这可能看起来很疯狂——你真的可以不将 IO 操作嵌入到 ScottyM
中吗?——但实际上这是有充分理由的。如果 IO
操作抛出错误会怎样?你的整个网络应用程序会崩溃吗?如果您天真地使用 liftIO
,它就会出现。相反,scotty 提供了函数 liftAndCatchIO
,正如文档所描述的那样,它“类似于 liftIO
,但捕获任何 IO 异常并将它们转换为 Scotty 异常。”这是将 IO
动作嵌入 Scotty 的首选方式。
最后的问题来了:请注意 liftAndCatchIO
实际上产生的是 ActionM a
类型的值,而不是 ScottyM a
。此外,无法在 ActionM
monad 中获取值并将其放入 ScottyM
monad。相反,您需要将该值用作操作。所以,我不确定 pathsToScotty
的作用,但很可能您需要重写它。
大家好,感谢您抽出宝贵时间。
我有一个错误,我不确定错误是什么,但我认为问题是:
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
,为什么它不起作用?也就是说,您有一个 IO [String]
类型的值 getAllFilePaths2 path
,并且您希望它是 ScottyM [String]
类型的值——这确实是使用 [=10] 的好地方=].但是,ScottyM
不是 MonadIO
的实例,因为您看到的错误消息试图告诉您,所以您不能使用 liftIO
.
这可能看起来很疯狂——你真的可以不将 IO 操作嵌入到 ScottyM
中吗?——但实际上这是有充分理由的。如果 IO
操作抛出错误会怎样?你的整个网络应用程序会崩溃吗?如果您天真地使用 liftIO
,它就会出现。相反,scotty 提供了函数 liftAndCatchIO
,正如文档所描述的那样,它“类似于 liftIO
,但捕获任何 IO 异常并将它们转换为 Scotty 异常。”这是将 IO
动作嵌入 Scotty 的首选方式。
最后的问题来了:请注意 liftAndCatchIO
实际上产生的是 ActionM a
类型的值,而不是 ScottyM a
。此外,无法在 ActionM
monad 中获取值并将其放入 ScottyM
monad。相反,您需要将该值用作操作。所以,我不确定 pathsToScotty
的作用,但很可能您需要重写它。