将类型参数限制为 Monoid

Restricting type parameter to Monoid

我之前定义了一个函数,它接受 Maybe 的列表并将其转换为列表的 Maybe,如下所示:

floop :: [Maybe a] -> Maybe [a]
floop [] = Just []
floop (Nothing:_) = Nothing
floop (Just x:xs) = fmap (x:) $ floop xs

现在我想重新定义它以兼容更大的 class 容器,而不仅仅是列表,我发现它需要实现函数 foldrmappendmemptyfmappure;所以我认为以下类型行是合适的:

floop :: (Foldable t, Functor t, Monoid t) => t (Maybe a) -> Maybe (t a)

因为(我认为)它确保为给定的容器实现这些功能,但是它会导致以下错误:

Expecting one more argument to ‘t’
The first argument of ‘Monoid’ should have kind ‘*’,
  but ‘t’ has kind ‘* -> *’
In the type signature for ‘floop'’:
  floop' :: (Foldable t, Functor t, Monoid t) =>
            t (Maybe a) -> Maybe (t a)

看了之后,我发现Monoid的种类和FunctorFoldable的种类不一样,但是我不明白为什么会这样,也不知道如何纠正错误。

对于那些感兴趣的人,这是当前的实现:

floop :: (Foldable t, Functor t, Monoid t) => t (Maybe a) -> Maybe (t a)
floop xs = let
                f :: (Foldable t, Functor t, Monoid t) => Maybe a -> Maybe (t a) -> Maybe (t a)
                f Nothing _ = Nothing
                f (Just x) ys = fmap (mappend $ pure x) ys
            in
                foldr f (Just mempty) xs

注意:我知道这已经作为内置函数存在 (sequence),但我打算将其作为学习练习来实现。

Monoidal 应用程序由 Alternative class 描述,使用 (<|>)empty 而不是 mappendmempty:

floop :: (Foldable t, Alternative t) => t (Maybe a) -> Maybe (t a)
floop xs = let
                f :: (Foldable t, Alternative t) => Maybe a -> Maybe (t a) -> Maybe (t a)
                f Nothing _ = Nothing
                f (Just x) ys = fmap ((<|>) $ pure x) ys
            in
                foldr f (Just empty) xs 

这可能是提出 hoogle 的好地方。

正在搜索 t (m a)-> m (t a) returns sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a) 作为第一个结果。然后这导致 Traversable 类型 class 非常接近您正在寻找的类型。

正如 Lee 所说,您可以使用 Alternative class,它是 Monoid 的应用等价物。更笼统一点:

sequence' :: (Alternative t, Foldable t, Applicative a) => t (a b) -> a (t b)
sequence' = foldr step (pure empty)
  where step = liftA2 prepend
        prepend = (<|>) . pure

这里prepend 首先将一些单个元素包装到t 中,因此它可以使用(<|>) 来添加它。 liftA2 让我们对应用 a 进行抽象,您可以将其想象为展开两个参数,将它们应用于前置并将结果包装起来。