在Haskell中是否存在表达以下代码的惯用方法?
main :: IO ()
main = loop initState1 initState2 initState3
loop :: State1 -> State2 -> State3 -> IO ()
loop s1 s2 s3 = do
s1' <- runService1 s1
s2' <- runService2 s2
s3' <- runService3 s3
loop s1' s2' s3'
Run Code Online (Sandbox Code Playgroud)
这段代码非常冗长,所以我可能会做一些奇怪的事情.
main = fix (zipWithM ($) >=>)
[runService1, runService2, runService3]
[initState1 , initState2 , initState3 ]
Run Code Online (Sandbox Code Playgroud)
比较fix . (>>=) :: IO a -> IO b,这是forever.
编辑:这仅在State1= State2=时有效State3.如果没有,data-fix允许:
main = fix (traverse unFix >=>)
[ana runService1 initState1, ana runService2 initState2, ana runService3 initState3]
Run Code Online (Sandbox Code Playgroud)
我的做法是,您可能希望以适当的状态monad来推动该状态。该lens库使访问它变得容易:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens.TH
import Control.Monad.Trans.State
data AllState = AllState { _s? :: State0, _s? :: State1, _s? :: State2 }
makeLenses ''AllState
loop :: StateT AllState IO ()
loop = do
s? <~ runService0 <$> use s?
s? <~ runService1 <$> use s?
s? <~ runService2 <$> use s?
loop
main = evalStateT loop $ AllState initState0 initState1 initState2
Run Code Online (Sandbox Code Playgroud)
这样一来,您不会在原始代码上花太多钱,但是如果您还为runService动作指定合适的state-monad类型,它将变得更加方便:
runService0 :: StateT State0 IO ()
runService1 :: StateT State1 IO ()
runService2 :: StateT State2 IO ()
Run Code Online (Sandbox Code Playgroud)
...然后您可以简单地使用以下zoom机制:
loop :: StateT AllState IO ()
loop = do
zoom s? runService0
zoom s? runService1
zoom s? runService2
loop
Run Code Online (Sandbox Code Playgroud)
或古尔肯拉斯(Gurkenglas)建议
loop = forever $ do
zoom s? runService0
zoom s? runService1
zoom s? runService2
Run Code Online (Sandbox Code Playgroud)