[英]Idiomatic stateful loop in Haskell
在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'
這段代碼非常冗長,所以我可能會做一些奇怪的事情。
main = fix (zipWithM ($) >=>)
[runService1, runService2, runService3]
[initState1 , initState2 , initState3 ]
比較fix . (>>=) :: IO a -> IO b
fix . (>>=) :: IO a -> IO b
,這是forever
。
編輯:這僅在State1
= State2
= State3
。 如果沒有, data-fix
允許:
main = fix (traverse unFix >=>)
[ana runService1 initState1, ana runService2 initState2, ana runService3 initState3]
我練習,你可能想把這個狀態推到合適的狀態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
因此,這並不會對原始代碼產生太大影響,但如果您還為runService
操作提供了合適的state-monad類型,那么它會變得更加方便:
runService0 :: StateT State0 IO ()
runService1 :: StateT State1 IO ()
runService2 :: StateT State2 IO ()
...然后你可以簡單地使用zoom
機制:
loop :: StateT AllState IO ()
loop = do
zoom s₀ runService0
zoom s₁ runService1
zoom s₂ runService2
loop
或者像Gurkenglas所說的那樣
loop = forever $ do
zoom s₀ runService0
zoom s₁ runService1
zoom s₂ runService2
這種解決方案並不簡潔,但解決了原始配方的不足之處:每個過程的“展開”與所有過程的“拉鏈”混合在一起。 如果我們可以獨立定義每個過程,並在我們認為合適的時候將它們組合起來將會很好。
我們需要以下輔助類型:
newtype Iter = Iter (IO Iter)
unfoldIter :: (s -> IO s) -> s -> Iter
unfoldIter f s = Iter (unfoldIter f <$> f s)
runIter :: Iter -> IO ()
runIter (Iter action) = action >>= runIter
doNothingIter :: Iter
doNothingIter = unfoldIter return ()
zipIter :: Iter -> Iter -> Iter
zipIter (Iter action1) (Iter action2) =
Iter (zipIter <$> action1 <*> action2)
instance Monoid Iter where
mempty = doNothingIter
mappend = zipIter
然后loop
變為:
loop :: State1 -> State2 -> State3 -> IO ()
loop s1 s2 s3 =
runIter $ unfoldIter runService1 s1
<> unfoldIter runService2 s2
<> unfoldIter runService3 s3
如果我們不想定義自己的輔助類型,我們可以使用提供“壓縮”操作的流式庫,例如流式傳輸 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.