【问题标题】:Haskell Monads and the liftIO I don't get itHaskell Monads 和 liftIO 我不明白
【发布时间】:2021-02-04 10:00:39
【问题描述】:

社区您好,感谢您抽出宝贵时间。

我有一个错误,我不确定错误是什么,但我认为问题是: 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)

【问题讨论】:

  • 次要:您误读了错误消息。其形式为No instance for (MonadIO (X Y Z)),其中X=Web.Scotty.Internal.Types.ScottyTY=text-1.2.4.1:Data.Text.Internal.Lazy.TextZ=IO。在您的问题中,您提到 Y Z 好像那是一种类型,但事实并非如此。 ZX 的第二个参数,而不是Y 的第一个参数(不带参数)。
  • ScottyM 必须是 MonadIO 的一个实例。如果它是 IO 上的 monad 转换器,您应该能够推导出

标签: haskell monads io-monad scotty lifting


【解决方案1】:

真正理解 monads 需要时间、练习和耐心,但通过检查你的类型来理解对 liftIO 的需求应该不会太难。

首先,liftIO 的类型是MonadIO m =&gt; IO a -&gt; m a。这意味着该函数可以将任何 IO 操作转换为 monad m 中的操作,只要 m 具有 MonadIO 的实例。理论上,这只能在m 有某种方式处理IO 动作的情况下才能实现,所以这个函数将给定的动作嵌入到m monad 中。

您绝对是在正确的地方使用liftIO,那么为什么它不起作用?也就是说,你有一个IO [String] 类型的值getAllFilePaths2 path,并且你希望它是ScottyM [String] 类型的值——这确实是一个使用liftIO 的好地方。但是,ScottyM不是MonadIO 的实例,因为您看到的错误消息试图告诉您,所以您不能使用liftIO

这可能看起来很疯狂——你真的不能将 IO 操作嵌入到 ScottyM 中吗?——但这实际上是有充分理由的。如果IO 操作抛出错误会发生什么?你的整个网络应用程序崩溃了吗?如果您天真地使用liftIO,它会。相反,scotty 提供了函数liftAndCatchIO,正如文档所描述的,它是“类似于liftIO,但捕获任何 IO 异常并将它们转换为 Scotty 异常”。这是将 IO 操作嵌入到 Scotty 的首选方式。

最后的问题来了:注意liftAndCatchIO 实际上产生ActionM a 类型的值,而不是ScottyM a。此外,没有办法在ActionM monad 中获取一个值并将其放入ScottyM monad。相反,您需要将该值用作操作。所以,我不确定pathsToScotty 做了什么,但很可能你需要重写它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多