这不应该在图书馆的某个地方吗?

时间:2016-12-05 21:00:32

标签: haskell

我正在编写一些代码来生成大量随机值并将它们添加到结构中,我需要一种机制来链接相同的IO操作。所以,我写了这个:

chain :: Monad m => Int -> (a -> m a) -> a -> m a
chain 0 _ a = return a
chain n f a = f =<< chain (n-1) f a

我怀疑这应该在某个库的某个地方,但我在Control.Monad中找不到它。这个功能已经写好了吗?有更简单的方法吗?

4 个答案:

答案 0 :(得分:6)

如果我要建议使用库,我建议为以下新类型添加SemigroupMonoid个实例:

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
import Control.Monad
import GHC.Prim (coerce)

newtype KleisliEndo m a = KE { runKE :: a -> m a }
instance Monad m => Monoid (KleisliEndo m a) where
    mempty = coerce (return @m @a)
    mappend = coerce ((>=>) @m @a @a @a)

然后stimesMonoid就是你的行动。

我不会惊讶地发现这个新类型确实存在于某个库中。

答案 1 :(得分:6)

@duplode(涉及foldr)提出的答案当然是正确的。但是,在这里必须在foldlfoldr之间进行选择,这有点令人讨厌,因为真正的(>=>)实际上是关联的。实际上,这只是 begging 使用fold :: Monoid m => t m -> m来解决。

不幸的是,newtype中的这个monoid没有Data.Monoid包装器。最接近的是Endo,它有点短。但是,对于Endomorphism包中的monoid-extras,我们可以生成有用的composeN函数@jpath建议您将函数推广到Category

composeN :: Category cat => Int -> cat a a -> cat a a
composeN n = getEndomorphism . foldMap Endomorphism . replicate n

然后我们真正想要的是composeN用于Kleisli类别

chain :: Monad m => Int -> (c -> m c) -> c -> m c
chain n = runKleisli . composeN n . Kleisli

修改

关注@Daniel Wagners回答 - Endomorphism是你需要的幺半群。我认为这样可以获得:

import Data.Monoid.Endomorphism (Endomorphism(..))
import Data.Semigroup (stimesMonoid)
import Control.Arrow (Kleisli(..))

chain :: Monad m => Int -> (c -> m c) -> c -> m c
chain n = (runKleisli . getEndomorphism) . stimesMonoid n . (Endomorphism . Kleisli)

如果您不反对启用语言扩展,则可以使用强制实际删除runKleisli . getEndomorphismEndomorphism . Kleisli

{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}

import Data.Monoid.Endomorphism (Endomorphism(..))
import Data.Semigroup (stimesMonoid)
import Control.Arrow (Kleisli(..))
import Data.Coerce (coerce)

chain :: forall m c. Monad m => Int -> (c -> m c) -> c -> m c
chain = coerce (stimesMonoid @Int @(Endomorphism (Kleisli m) c))

答案 2 :(得分:4)

我不知道它是否存在于某个地方,但它可以巧妙地定义为:

chain :: Monad m => Int -> (c -> m c) -> c -> m c
chain n = foldr (>=>) return . replicate n

monad-loops有一些潜在有用的功能(foldr (>=>) return,例如,concatM),但不完全是chain

答案 3 :(得分:3)

对于此类情况,最好在Stackage,Hoogle Hayoo上搜索您想要的类型。在这种情况下,您可以在Hayoo上找到two results,在Stackage上找到some more。但是在这种情况下,我想我会使用@ duplode的解决方案,而不是依赖于另一个包(除非你喜欢其中一个包中的很多功能)。

如果您在这些网站上搜索,另一个提示是考虑可能的功能概括。例如,在这种情况下,如果一个人愿意与Kleisli newtype包装器共轭,那么这样的函数也可以工作:

 composeN :: Category cat => Int -> cat a a -> cat a a

可悲的是,我找不到这样的功能。

Categorie s是可以组成并具有身份的东西。)