为什么使用`elem`解决方案需要`Eq a`约束?

tor*_*tte 7 haskell

我正在从Haskell书中解决以下练习:

-- >>> 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
Run Code Online (Sandbox Code Playgroud)

首先我尝试使用elem,

flipMaybeWithElem :: [Maybe a] -> Maybe [a]
flipMaybeWithElem ms
  | Nothing `elem` ms = Nothing
  | otherwise         = Just (catMaybes ms)
Run Code Online (Sandbox Code Playgroud)

但是我收到了错误消息:

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
   |     ^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

我知道我应该只将Eq a =>约束添加到函数签名中,但我试图保持对提供的函数存根的忠诚.所以我重用了以前的功能,它确实有效:

flipMaybe :: [Maybe a] -> Maybe [a]
flipMaybe ms =
  case (filter isNothing ms) of
    [] -> Just (catMaybes ms)
    _  -> Nothing
Run Code Online (Sandbox Code Playgroud)

使用的辅助函数:

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)
Run Code Online (Sandbox Code Playgroud)

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

它只是因为filter并且isNothing对类型变量没有约束而且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 
Run Code Online (Sandbox Code Playgroud)

Maybe有一个Eq实例,但我想编译器对此一无所知a,对吧?

Sil*_*olo 9

它只是因为filter并且isNothing对类型变量没有约束而且elem有吗?(同样,isNothing类型变量甚至从未发挥作用,因为它被忽略了.)

你在这里击中了钉子.elem,一般来说,只会工作,如果Eq a是满意的.您正尝试使用它[Maybe a],并Maybe a具有以下Eq实例.

instance Eq a => Eq (Maybe a) where
    ...
Run Code Online (Sandbox Code Playgroud)

所以elem看着你Maybe a说"我需要这个Eq".它看到上面的例子并说"为了Maybe a满足Eq,a必须满足Eq,我不知道Eq a".现在,在您的特定情况下,您只需进行比较Nothing,因此Eq a实例永远不会被实际使用.但编译器没有足够的信息来了解这一点; 它只知道elem需求Eq a,而你没有提供这种约束.

为了让这个函数无需工作Eq,你需要按照你已经做过的方式去做,无论是使用显式递归还是使用filterisNothing.简而言之,我认为你已经找到了答案,我只是重申你已经说过的话.


Dan*_*ner 6

它只是因为filter并且isNothing对类型变量没有约束而且elem有吗?(同样,isNothing类型变量甚至从未发挥作用,因为它被忽略了.)

正确,你在这里钉了它.

Maybe有一个Eq实例,但我想编译器对此一无所知a,对吧?

又把它钉了起来.