我之前已经定义了一个函数,该函数获取Maybe
的列表并将其转换为列表的floop :: [Maybe a] -> Maybe [a]
floop [] = Just []
floop (Nothing:_) = Nothing
floop (Just x:xs) = fmap (x:) $ floop xs
,如下所示:
foldr
现在我想重新定义它以与更大类的容器兼容,而不仅仅是列表,我发现它需要实现函数mappend
,mempty
,{{1 }},fmap
和pure
;所以我认为以下类型行是合适的:
floop :: (Foldable t, Functor t, Monoid t) => t (Maybe a) -> Maybe (t a)
As(我认为)它确保为给定容器实现这些功能,但是它会导致以下错误:
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
的种类与Functor
和Foldable
的种类不同,但我不明白为什么会出现这种情况,也不知道如何纠正错误。
对于那些感兴趣的人,这是当前的实施:
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
)存在,但我打算将其作为一个学习练习来实现。
答案 0 :(得分:9)
Monoidal applicatives由Alternative
类描述,使用(<|>)
和empty
代替mappend
和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
答案 1 :(得分:3)
这可能是提出hoogle的好地方。
搜索t (m a)-> m (t a)
返回sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
作为第一个结果。然后,这将导致Traversable类型类与您正在寻找的类型非常接近。
正如Lee所说,你可以使用Alternative类,它类似于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中,这样它就可以使用(&lt; |&gt;)来预先添加它。 liftA2让我们抽象出应用程序a,您可以将它想象为解开两个参数,将它们应用于前置并将结果包装回来。