有一堆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
我了解,但是我仍然想知道我是否可以使它工作。
答案 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]