簡體   English   中英

Haskell中的慣用狀態循環

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM