将变量传递给IO中的多个顺序函数

nbr*_*brr 2 haskell

有一堆IO计算,其结果无关紧要,并且依赖于相同的上下文变量.我希望以一种美学的方式将这个论点传递给所有函数.

假设我想多次打印一个char

c = 'c'
main = do
  putChar c
  putChar c
  putChar c
Run Code Online (Sandbox Code Playgroud)

但我不想每次都写这个参数.

以下工作正常:

samearg :: (Foldable t, Monad m) => t (a -> m ()) -> a -> m ()
samearg fs ctx = foldl (\d f -> d >>= \_ -> f ctx) (return ()) fs
(>^=) = flip samearg

'c' >^= [putChar,putChar,putChar]
Run Code Online (Sandbox Code Playgroud)

现在,我只是好奇我是否可以根据我最初的想法写出一些东西,或者是否有一些标准的方法来做到这一点.我想写一些类似的东西'c' >^= putChar >^= putChar >^= putChar会减少到这一点

((return 'c' >>= putChar >>= \_ -> return 'c')
>>= putChar >>= \_ -> return 'c')
>>= putChar
Run Code Online (Sandbox Code Playgroud)

但是我写的这个算子并没有减少到我的预期

(>^=) :: Monad m => m b -> (b -> m a) -> m b
(>^=) ctx f = ctx >>= f >>= \_ -> ctx

return 'c' >^= putChar >^= putChar >^= putChar
Run Code Online (Sandbox Code Playgroud)

我明白了,但我仍然想知道我是否能让它发挥作用.

che*_*ner 7

(这使得可能过于乐观的假设所有函数都具有返回类型IO ().)


putChar有类型Char -> IO ().这里有三种类型,所有类型都有Monoid实例:

  • (自base-4.9)() <> () == ()
  • (因为base-4.10)如果b是幺半群,那么IO b也是如此; m1 <> m2 == (liftA2 (<>)) m1 m2(返回IO执行原始IO动作的结果的新动作).
  • (因为base-4.9)If b是一个monoid,那么对于f,g :: a -> b我们有f <> g == \x -> f x <> g x(两个函数都在相同的参数上调用,并且返回值被组合.

把这一切放在一起,类型的函数Char -> IO ()形成一个幺半群.

> :t putChar <> putChar <> putChar
putChar <> putChar <> putChar :: Char -> IO ()
> (putChar <> putChar <> putChar) 'c'
ccc>
Run Code Online (Sandbox Code Playgroud)

所以你可以简单地写

main = putChar <> putChar <> putChar $ c
Run Code Online (Sandbox Code Playgroud)

要么

main = mconcat (replicate 3 putChar) $ c
Run Code Online (Sandbox Code Playgroud)