我正在编写一些代码来生成大量随机值并将它们添加到结构中,我需要一种机制来链接相同的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中找不到它。这个功能已经写好了吗?有更简单的方法吗?
答案 0 :(得分:6)
如果我要建议使用库,我建议为以下新类型添加Semigroup
和Monoid
个实例:
{-# 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
)提出的答案当然是正确的。但是,在这里必须在foldl
和foldr
之间进行选择,这有点令人讨厌,因为真正的(>=>)
实际上是关联的。实际上,这只是 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 . getEndomorphism
和Endomorphism . 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是可以组成并具有身份的东西。)