模式匹配中的Monoid mempty

时间:2012-08-31 13:58:05

标签: haskell pattern-matching monoids

我尝试编写类似于maximum中的广义Prelude函数。我的第一个naiv方法看起来像这样:
maximum' :: (F.Foldable a, Ord b) => a b -> Maybe b
maximum' mempty = Nothing
maximum' xs = Just $ F.foldl1 max xs

然而,当我测试它时,无论输入如何,它总是返回Nothing
> maximum' [1,2,3]
> Nothing

现在我想知道是否有可能获得Monoid类型实例的空值。我写的测试函数正常工作:
getMempty :: (Monoid a) => a -> a
getMempty _ = mempty

> getMempty [1,2,3]
> []

我已经看过这两个问题,但我没有弄清楚答案如何解决我的问题:
Write a Maximum Monoid using Maybe in Haskell
Haskell Pattern Matching on the Empty Set

如何重写maximum'函数才能使其正常工作?

5 个答案:

答案 0 :(得分:9)

正如C. A. McCann在评论中指出的那样,你不能模仿价值观,只能模式匹配。

等式maximum' mempty = Nothing实际上等同于等式maximum' x = Nothing。该参数绑定到一个名称,并返回Nothing

这是让您的代码有效的方法:

maximum' :: (F.Foldable a, Ord b, Eq (a b), Monoid (a b)) => a b -> Maybe b
maximum' xs
  | xs == mempty = Nothing
  | otherwise    = Just $ F.foldl1 max xs

即。您可以比较xsmempty。请注意,我们需要Monoid约束才能获得值mempty :: a bEq约束以便能够进行比较。

另一个更优雅的解决方案是使用折叠来区分空和非空案例:

maximum'' :: (F.Foldable a, Ord b) => a b -> Maybe b
maximum'' xs = F.foldl max' Nothing xs
  where max' Nothing x = Just x
        max' (Just y) x = Just $ max x y

答案 1 :(得分:4)

有几种方法可以做到这一点(@opqdonut演示的那个很好)。也可以在Maybe附近制作一个“最大”的幺半群,并使用foldMap

newtype Maximum a = Max { unMaximum :: Maybe a }

instance (Ord a) => Monoid (Maximum a) where
  mempty = Max Nothing
  mappend (Max Nothing) b = b
  mappend a (Max Nothing) = a
  mappend (Max (Just a)) (Max (Just b)) = Max . Just $ (max a b)

maximum' = unMaximum . F.foldMap (Max . Just)

答案 2 :(得分:3)

有很多种方法,一种是(正如你所提到的)创建Monoid的实例。但是,我们需要将其包装到Maybe以区分我们没有值的情况。实现可能如下所示:

import Data.Monoid (Monoid, mempty, mappend)
import qualified Data.Foldable as F

-- Either we have a maximum value, or Nothing, if the
-- set of values is empty.
newtype Maximum a = Maximum { getMaximum :: Maybe a }
    deriving (Eq, Ord, Read, Show)

instance Ord a => Monoid (Maximum a) where
    mempty                      = Maximum Nothing

    -- If one part is Nothing, just take the other one.
    -- If both have a value, take their maximum.
    (Maximum Nothing) `mappend` y    = y
    x `mappend` (Maximum Nothing)    = x
    (Maximum (Just x)) `mappend` (Maximum (Just y))
                                     = Maximum (Just $ x `max` y)


maximum' :: (F.Foldable t, Ord a) => t a -> Maximum a
maximum' = F.foldMap (Maximum . Just)

答案 3 :(得分:2)

正如许多人已经告诉过你的那样,你不能对一个值进行模式匹配。

正如人们告诉你的那样,模式匹配可以说是Haskell等同于Java语言中的对象字段:它对于紧密耦合代码的内部消费很有价值,但可能不是你希望暴露给外部客户端代码的东西。基本上,如果你让一段代码知道你的类型的构造函数,那么现在你永远不能在不改变其他代码的情况下改变这些构造函数 - 即使你的类型的语义没有真正改变。

这里最好的解决方案就是使用Foldable.foldr

maximum' :: (F.Foldable a, Ord b) => a b -> Maybe b
maximum' = F.foldr step Nothing
    where step x Nothing = Just x
          step x (Just y) = Just (max x y)

请注意,foldrFoldable个实例的通用析构函数消除器:它的两个参数是“如何处理非清空Foldable“和”如何处理mempty。这比模式匹配更抽象,更可重用。

答案 4 :(得分:1)

怎么样

maximum' :: (Monoid (t a), F.Foldable t, Ord a, Eq (t a)) => t a -> Maybe a
maximum' xs
   | xs == mempty = Nothing
   | otherwise    = Just $ F.foldl1 max xs

你错过了一名警卫。

getEmpty功能上,您不需要它。只需使用mempty,并允许推断其类型。