首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >failure包中的`Failure`的‘from e`实例出现问题

failure包中的`Failure`的‘from e`实例出现问题
EN

Stack Overflow用户
提问于 2012-01-19 09:55:07
回答 1查看 91关注 0票数 2

这或多或少是我在自己的代码中试图理解的问题的简化版本。我正在使用来自this packageFailure类中的多态函数。

代码语言:javascript
复制
{-# LANGUAGE FlexibleContexts #-}
import Data.Maybe
import Data.Either
import Control.Failure

data FookError = FookError deriving Show

fook :: (Failure FookError m)=> Int -> m Int
fook = undefined

fooks :: (Failure FookError m)=> Int -> m Int
-- DOES NOT TYPE-CHECK:
--fooks n = return $ head $ rights $ map fook [1..]
-- OKAY:
fooks n   = return $ head $ catMaybes $ map fook [1..]

您可以在上面的代码中看到,当我将fook的返回类型视为Maybe Int时,模块编译得很好,但是将其视为Either Fook Int失败了。

这里发生什么事情?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-01-19 10:06:15

这是因为,在fooks的非工作定义中,fook的类型是不明确的。当你使用catMaybes时,它消除了Maybe Int的歧义,但当你使用rights时,对于任何代码来说,它都可以是Either e Int,编译器不一定知道是哪一个。当然,默认情况下,Failure的唯一Either实例是<代码>D9,但是没有什么能阻止你定义,例如<代码>D10。

如果通过将fooks定义为显式指定类型

代码语言:javascript
复制
fooks n =
  return $ head $ rights $ map (fook :: Int -> Either FookError Int) [1..]

那么它工作得很好。

但是,我怀疑您在这里做的并不是您真正想做的事情;fooks实际上从未使用过底层monad的失败功能;实际上,即使没有不失败的结果,monadic操作仍然会成功并返回值。这个值恰好是一个错误,但这可能仍不是您想要的:)

如果您希望fooks依次尝试一组单独的fook,并返回第一个成功的try,则如下所示:

代码语言:javascript
复制
fooks :: (Failure FookError m, MonadPlus m) => Int -> m Int
fooks n = foldr mplus (failure FookError) $ map fook [1..]

应该能行得通。普通的Failure类本身不提供从错误中恢复的方法,因此您也需要MonadPlus。请注意,这里的failure FookError永远不会实际使用,因为[1..]是无限的,但是您可能计划更改定义;假设是实际使用n的定义:)

不幸的是,这还不是全部!Either e没有MonadPlus实例,大概是因为mzero没有合理的值(尽管另一个潜在问题是mplus (Left e) (Left e')可能是Left eLeft e')。

值得庆幸的是,很容易为我们的特定类型定义一个实例:

代码语言:javascript
复制
instance MonadPlus (Either FookError) where
  mzero = failure FookError
  mplus a@(Right _) _ = a
  mplus (Left _) a = a

要做到这一点,您需要在文件顶部使用{-# LANGUAGE FlexibleInstances #-}

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8920133

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档