haskelltypestype-kinds

Restricting type parameter to Monoid


I've previously defined a function which takes a list of Maybes and turns it into a Maybe of a list, like so:

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

Now I want to redefine it to be compatible with a larger class of containers, not just lists, and I've found that it needs to implement the functions foldr, mappend, mempty, fmap , and pure; so I figure that the following type line would be appropriate:

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

As (I think) it ensures that those functions are implemented for the given container, however it leads to the following error:

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)

After looking into it, I found Monoid's kind is different to that of Functor and Foldable, but I can't see why that would be the case, nor how to correct the error.

For those interested, here's the current implementation:

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

Note: I have been made aware that this already exists as a builtin function (sequence), but I intend to implement it as a learning exercise.


Solution

  • Monoidal applicatives are described by the Alternative class, using (<|>) and empty instead of mappend and mempty:

    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