将变量传递给IO中的多个顺序功能

时间:2019-01-07 20:59:23

标签: haskell

有一堆IO计算的结果无关紧要,它们依赖于相同的上下文变量。我想以一种美学的方式将此参数传递给所有函数。

说我要多次打印一个字符

c = 'c'
main = do
  putChar c
  putChar c
  putChar c

但是我不想每次都写参数。

以下工作正常:

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]

现在,我很好奇我是否可以按照最初的想法写东西,或者是否有一些标准的方法可以做到这一点。我想写类似'c' >^= putChar >^= putChar >^= putChar的东西,将其简化为

((return 'c' >>= putChar >>= \_ -> return 'c')
>>= putChar >>= \_ -> return 'c')
>>= putChar

但是我写的这个运算符并没有降低我的期望

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

return 'c' >^= putChar >^= putChar >^= putChar

我了解,但是我仍然想知道我是否可以使它工作。

2 个答案:

答案 0 :(得分: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起)如果b是一个等分线,那么对于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>

所以您可以简单地写

main = putChar <> putChar <> putChar $ c

main = mconcat (replicate 3 putChar) $ c

答案 1 :(得分:3)

这是(>^=)的固定版本

(>^=) :: Monad m => m b -> (b -> m a) -> m b
(>^=) ctx f = ctx >>= \y -> f y >> return y   -- only uses ctx once

您还可以使用现有的组合器将操作放在列表中并遍历它:

traverse_ (\go -> go 'c') [putChar, putChar, putChar]