2

我将运算符<|>用于:

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 ‘&lt;|>’
    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)

这个错误对我来说有点模糊。知道如何解决吗?(顺便说一句,从这个回复中得到一些见解:对“替代”类型类的含义及其与其他类型类的关系感到困惑

4

3 回答 3

3

除了您的实际问题之外,这就是为什么 IO 不能成为替代方案的原因。

首先,你会做什么empty?它需要有类型forall a. IO a——一个返回任何类型值的 IO 操作!

其次,你如何定义失败(用于<|>)?对于诸如 的类型Maybe,空是显而易见的 ( Nothing) 并且也可以定义失败Nothing。因此,如果第一个参数是 Nothing,则返回第二个。但是 IO 呢?monad 上失败的(令人困惑的)概念很神奇,并且没有在类型级别表示,换句话说,没有明确的失败值。

如果您将所有 IO 操作包装在(MaybeEither) 中,例如readFile' :: FilePath -> IO (Maybe String). 然后通过举起<|>你可以组合动作。您必须捕获失败(异常)并将它们转换为Nothing您的包装器。

Alternative(为方便起见,可以为所有人创建一个实例,Alternative f, Monad m => m f但这需要类型组合?我不确定)

于 2015-07-07T08:10:19.813 回答
2

正如您从文档中看到的那样,该错误只是说这IO不是一个实例,Alternative因此没有为它定义。您的意思是“尝试,如果失败,请改用”?如果是这样,你可以这样做<|>B.readFile foosimpleHttp bar

catch (B.readFile foo) (\(_ :: SomeException) -> simpleHttp bar)

catch来自Control.Exception,您应该使用Data.ByteString.Strict而不是Lazy确保在 的范围内引发异常catch;您还需要ScopedTypeVariables扩展来编写上述内容而不是类似的内容\e -> let _ = e :: SomeException in simpleHttp bar)。

于 2015-07-07T08:00:24.427 回答
2

以下是基于博客文章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

我在这个例子中非常明确 - 文章提到了可以减少样板代码数量的方法。

我的阴谋集团build-depends:设置:

build-depends: base >= 4.7 && < 5, bytestring, mtl, http-conduit, transformers-base
于 2015-07-07T08:57:13.657 回答