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

Chr*_*ard 2 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
Run Code Online (Sandbox Code Playgroud)

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

Dan*_*ner 6

如果我要建议一个库来放入它,我建议为以下newtype 添加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)
Run Code Online (Sandbox Code Playgroud)

然后stimesMonoid是你的操作.

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


Ale*_*lec 6

@duplode(涉及foldr)提出的答案当然是正确的.然而,人们必须在这里foldlfoldr这里之间做出选择,这有点令人讨厌,因为它确实(>=>)是真正的联想.事实上,这只是乞求用它来解决fold :: Monoid m => t m -> m.

不幸的是,newtype这个幺半群没有包装Data.Monoid.最接近的是Endo有点短.但是Endomorphismmonoid-extras包中我们可以生成有用的composeN函数@jpath建议您的函数一般化为Category:

composeN :: Category cat => Int -> cat a a -> cat a a
composeN n = getEndomorphism . foldMap Endomorphism . replicate n
Run Code Online (Sandbox Code Playgroud)

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

chain :: Monad m => Int -> (c -> m c) -> c -> m c
chain n = runKleisli . composeN n . Kleisli
Run Code Online (Sandbox Code Playgroud)

编辑

关注@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)
Run Code Online (Sandbox Code Playgroud)

如果您不反对启用语言扩展,您可以使用实际去除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))
Run Code Online (Sandbox Code Playgroud)