我正在解决 Haskell Book 中的以下练习:

-- >>> flipMaybe [Just 1, Just 2, Just 3]
-- Just [1, 2, 3]
-- >>> flipMaybe [Just 1, Nothing, Just 3]
-- Nothing

flipMaybe :: [Maybe a] -> Maybe [a]
flipMaybe = undefined

首先我尝试使用 elem
flipMaybeWithElem :: [Maybe a] -> Maybe [a]
flipMaybeWithElem ms
  | Nothing `elem` ms = Nothing
  | otherwise         = Just (catMaybes ms)

但我收到错误消息:
misc.hs:86:5: error:
    • No instance for (Eq a) arising from a use of ‘elem’
      Possible fix:
        add (Eq a) to the context of
          the type signature for:
            flipMaybe2 :: forall a. [Maybe a] -> Maybe [a]
    • In the expression: Nothing `elem` ms
      In a stmt of a pattern guard for
                     an equation for ‘flipMaybe2’:
        Nothing `elem` ms
      In an equation for ‘flipMaybe2’:
          flipMaybe2 ms
            | Nothing `elem` ms = Nothing
            | otherwise = Just (catMaybes ms)
   |
86 |   | Nothing `elem` ms = Nothing
   |     ^^^^^^^^^^^^^^^^^

我知道我应该只将 Eq a => 约束添加到函数签名中,但我试图忠于提供的函数 stub 。所以我重用了以前的功能,它确实有效:
flipMaybe :: [Maybe a] -> Maybe [a]
flipMaybe ms =
  case (filter isNothing ms) of
    [] -> Just (catMaybes ms)
    _  -> Nothing

使用的辅助函数:
isNothing :: Maybe a -> Bool
isNothing Nothing = True
isNothing _       = False

mayybee :: b -> (a -> b) -> Maybe a -> b
mayybee b _ Nothing = b
mayybee b f (Just a) = f a

fromMaybe :: a -> Maybe a -> a
fromMaybe a maybe = mayybee a id maybe

catMaybes :: [Maybe a] -> [a]
catMaybes xs = map (fromMaybe undefined) (filter isJust xs)

那么 为什么没有类型约束的 elem 的第一个解决方案不起作用?

是否仅仅是因为 filterisNothing 对类型变量没有约束而 elem 有? (还有 isNothing 类型变量甚至从未发挥作用,因为它被忽略了。)
> :t filter
filter :: (a -> Bool) -> [a] -> [a]

> :t isNothing
isNothing :: Maybe a -> Bool

> :t elem
elem :: (Eq a, Foldable t) => a -> t a -> Bool
Maybe 有一个 Eq 实例,但我猜编译器对 a 一无所知,对吧?

最佳答案



你在这里一针见血。 elem 通常只有在满足 Eq a 的情况下才会起作用。您正在尝试在 [Maybe a] 上使用它,并且 Maybe a 具有以下 Eq 实例。

instance Eq a => Eq (Maybe a) where
    ...

因此 elem 会查看您的 Maybe a 并说“我需要将其设为 Eq”。它看到上面的实例并说“为了 Maybe a 满足 Eqa 必须满足 Eq ,而我不知道 Eq a ”。现在,在您的特定情况下,您只与 Nothing 进行比较,因此从未实际使用 Eq a 实例。但是编译器没有足够的信息来知道这一点;它只知道 elem 需要 Eq a ,而您没有为其提供该约束。

为了让这个函数在没有 Eq 的情况下工作,你需要按照你已经做过的方式来做,要么使用显式递归,要么使用 filterisNothing 。总之,我想你已经找到答案了,我只是重申你已经说过的话。

关于haskell - 为什么使用 `Eq a` 的解决方案需要 `elem` 约束?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49519683/

10-16 12:42