<|> 的替代 IO 错误
Alternative IO error for <|>
我正在使用运算符 <|>
来:
import qualified Data.ByteString.Lazy as B
import Network.HTTP.Conduit (simpleHttp)
import Data.Aeson
import Data.Maybe
data FooBar = FooBar {
name :: !Text,
surname :: !Text
} deriving (Show,Generic)
instance FromJSON FooBar
instance ToJSON FooBar
getFeed :: String -> String -> IO (FooBar)
getFeed foo bar = decode <$> (B.readFile foo <|> simpleHttp bar)
但是当我尝试编译它时,我得到:
No instance for (Alternative IO) arising from a use of ‘<|>’
In the second argument of ‘(<$>)’, namely
‘(B.readFile foo <|> simpleHttp bar)’
In the expression:
decode <$> (B.readFile foo <|> simpleHttp bar)
In an equation for ‘getFeed’:
getFeed env id
= decode <$> (B.readFile foo <|> simpleHttp bar)
这个错误对我来说有点模糊。知道如何解决这个问题吗?
(顺便说一下,这个回复的一些见解:Confused by the meaning of the 'Alternative' type class and its relationship to other type classes)
错误只是说 IO
不是 Alternative
的实例,因此没有为它定义 <|>
,正如您从 documentation 中看到的那样。你的意思是 "try B.readFile foo
and if it fails, use simpleHttp bar
instead" 吗?如果是这样,你可以做
catch (B.readFile foo) (\(_ :: SomeException) -> simpleHttp bar)
(catch
来自 Control.Exception
,您应该使用 Data.ByteString.Strict
而不是 Lazy
以确保在 catch
范围内抛出异常; 你还需要 ScopedTypeVariables
扩展名来写上面的内容而不是 \e -> let _ = e :: SomeException in simpleHttp bar
).
除了你的实际问题,这就是 IO 不能替代的原因。
首先,您会为 empty
做什么?它需要具有类型 forall a. IO a
- 一个 IO 操作 return 任何类型的值!
其次,你如何定义失败(用于<|>
)?对于 Maybe
等类型,空是显而易见的(Nothing
),失败也可以定义为 Nothing
。因此,如果第一个参数为 Nothing,则 return 第二个。但是IO呢? monad 上的(混淆的)失败概念是神奇的,并且没有在类型级别表示,换句话说,没有明确的失败值。
如果您将所有 IO 操作包装在 Maybe
(或 Either
)中,它 可以 工作,例如 readFile' :: FilePath -> IO (Maybe String)
。然后通过提升 <|>
你可以组合动作。您必须捕获失败(异常)并将它们转换为包装器中的 Nothing
。
(为方便起见,可以为所有 Alternative f, Monad m => m f
创建一个 Alternative
的实例,但这需要类型组合吗?我不确定)
这是您可以根据博客执行的操作的示例 post Playing Catch: Handling IO Exceptions with ErrorT。
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Lib where
import qualified Data.ByteString.Lazy as B
import Network.HTTP.Conduit (simpleHttp)
import Control.Monad.Base
import Control.Applicative
import Control.Monad.Error
import System.IO.Error
newtype MyApp a = MyApp {
getApp :: ErrorT String IO a
} deriving (Functor, Applicative, Alternative, Monad, MonadIO, MonadError String, MonadBase IO)
myReadFile path = do
r <- liftIO $ tryIOError $ B.readFile path
case r of
Left e -> throwError (strMsg "readFile error")
Right x -> return x
mySimpleHttp bar = do
r <- liftIO $ tryIOError $ simpleHttp bar
case r of
Left e -> throwError (strMsg "simpleHttp error")
Right x -> return x
getFeed foo bar = myReadFile foo <|> mySimpleHttp bar
runApp = runErrorT . getApp
doit = do result <- runApp $ getFeed "some/file.txt" "http://example.com/"
case result of
Left e -> putStrLn $ "error: " ++ e
Right r -> do putStrLn $ "got a result"; print r
我在这个示例中已经非常明确 - 文章提到了可以减少样板代码数量的方法。
我的 cabal build-depends:
设置:
build-depends: base >= 4.7 && < 5, bytestring, mtl, http-conduit, transformers-base
我正在使用运算符 <|>
来:
import qualified Data.ByteString.Lazy as B
import Network.HTTP.Conduit (simpleHttp)
import Data.Aeson
import Data.Maybe
data FooBar = FooBar {
name :: !Text,
surname :: !Text
} deriving (Show,Generic)
instance FromJSON FooBar
instance ToJSON FooBar
getFeed :: String -> String -> IO (FooBar)
getFeed foo bar = decode <$> (B.readFile foo <|> simpleHttp bar)
但是当我尝试编译它时,我得到:
No instance for (Alternative IO) arising from a use of ‘<|>’
In the second argument of ‘(<$>)’, namely
‘(B.readFile foo <|> simpleHttp bar)’
In the expression:
decode <$> (B.readFile foo <|> simpleHttp bar)
In an equation for ‘getFeed’:
getFeed env id
= decode <$> (B.readFile foo <|> simpleHttp bar)
这个错误对我来说有点模糊。知道如何解决这个问题吗? (顺便说一下,这个回复的一些见解:Confused by the meaning of the 'Alternative' type class and its relationship to other type classes)
错误只是说 IO
不是 Alternative
的实例,因此没有为它定义 <|>
,正如您从 documentation 中看到的那样。你的意思是 "try B.readFile foo
and if it fails, use simpleHttp bar
instead" 吗?如果是这样,你可以做
catch (B.readFile foo) (\(_ :: SomeException) -> simpleHttp bar)
(catch
来自 Control.Exception
,您应该使用 Data.ByteString.Strict
而不是 Lazy
以确保在 catch
范围内抛出异常; 你还需要 ScopedTypeVariables
扩展名来写上面的内容而不是 \e -> let _ = e :: SomeException in simpleHttp bar
).
除了你的实际问题,这就是 IO 不能替代的原因。
首先,您会为 empty
做什么?它需要具有类型 forall a. IO a
- 一个 IO 操作 return 任何类型的值!
其次,你如何定义失败(用于<|>
)?对于 Maybe
等类型,空是显而易见的(Nothing
),失败也可以定义为 Nothing
。因此,如果第一个参数为 Nothing,则 return 第二个。但是IO呢? monad 上的(混淆的)失败概念是神奇的,并且没有在类型级别表示,换句话说,没有明确的失败值。
如果您将所有 IO 操作包装在 Maybe
(或 Either
)中,它 可以 工作,例如 readFile' :: FilePath -> IO (Maybe String)
。然后通过提升 <|>
你可以组合动作。您必须捕获失败(异常)并将它们转换为包装器中的 Nothing
。
(为方便起见,可以为所有 Alternative f, Monad m => m f
创建一个 Alternative
的实例,但这需要类型组合吗?我不确定)
这是您可以根据博客执行的操作的示例 post Playing Catch: Handling IO Exceptions with ErrorT。
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Lib where
import qualified Data.ByteString.Lazy as B
import Network.HTTP.Conduit (simpleHttp)
import Control.Monad.Base
import Control.Applicative
import Control.Monad.Error
import System.IO.Error
newtype MyApp a = MyApp {
getApp :: ErrorT String IO a
} deriving (Functor, Applicative, Alternative, Monad, MonadIO, MonadError String, MonadBase IO)
myReadFile path = do
r <- liftIO $ tryIOError $ B.readFile path
case r of
Left e -> throwError (strMsg "readFile error")
Right x -> return x
mySimpleHttp bar = do
r <- liftIO $ tryIOError $ simpleHttp bar
case r of
Left e -> throwError (strMsg "simpleHttp error")
Right x -> return x
getFeed foo bar = myReadFile foo <|> mySimpleHttp bar
runApp = runErrorT . getApp
doit = do result <- runApp $ getFeed "some/file.txt" "http://example.com/"
case result of
Left e -> putStrLn $ "error: " ++ e
Right r -> do putStrLn $ "got a result"; print r
我在这个示例中已经非常明确 - 文章提到了可以减少样板代码数量的方法。
我的 cabal build-depends:
设置:
build-depends: base >= 4.7 && < 5, bytestring, mtl, http-conduit, transformers-base