haskell - 'Either e` 包中 `Failure` 的 "failure"实例存在问题

标签 haskell types polymorphism

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

{-# 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 失败。

这是怎么回事?

最佳答案

这是因为,在 fooks 的非工作定义中,fook 的类型不明确。当您使用 catMaybes 时,它会消除歧义为 Maybe Int,但当您使用 rights 时,它可以是 Either e Int > 对于 any e,编译器不一定知道是哪一个。当然,默认情况下,Either 的唯一 Failure 实例是 instance Failure e (Either e),但是没有什么可以阻止您定义,例如实例失败字符串(整数)

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

fooks n =
  return $ head $ rights $ map (fook :: Int -> Either FookError Int) [1..]

然后就可以正常工作了。

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

如果您希望 fooks 依次尝试一堆单独的 fook,并返回第一个成功的,则类似于:

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')。

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

instance MonadPlus (Either FookError) where
  mzero = failure FookError
  mplus a@(Right _) _ = a
  mplus (Left _) a = a

您需要在文件顶部添加 {-# LANGUAGE FlexibleInstances #-} 才能执行此操作。

关于haskell - 'Either e` 包中 `Failure` 的 "failure"实例存在问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8920133/

相关文章:

haskell - 将 Kleisli 箭头提升到 IO?

haskell - 刚性类型变量麻烦/可疑

python - 在使用 `types.new_class` 创建的类上设置模块

oop - 多态性、重载和覆盖是相似的概念吗?

haskell - 将语义应用于自由单子(monad)

haskell - 如何使用没有 SomeException 作为参数类型的处理程序

python - OpenCV Python 特征检测 : how to provide a mask? (SIFT)

c# - 具有多个 child 的通用基类

c++ - 返回bitset的成员函数,继承自基类

haskell - 使用 IxSet,我可以围绕任意可索引类型构建可索引包装吗?