[英]Haskell - Mixed stateful computations
鑒於以下結構:
data Computation a = Pure (CState -> (a, CState))
| Action (CState -> IO (a, CState))
(CState是一種用於保持國家的結構,但現在並不是很感興趣。)
現在我想讓它成為Monad的一個實例,它基本上只是一個狀態monad,可以很容易地用StateT實現。 它唯一的補充是,我想跟蹤,無論結果Computation是Pure還是Action,我希望能夠在執行Action
之前檢查Computation是否包含任何Action
,所以IO in Action
沒有被執行)。
還應該指出, Computation
有兩個構造函數並不重要。 我剛開始用這些構造函數實現它。
判斷a >> b
是否為純的規則很簡單: a >> b
是Pure
如果a
和b
都是Pure
,否則它就是一個動作。
現在我開始實現Monad實例:
instance Monad Computation where
return x = Pure $ \s -> (x, s)
(Action c) >>= f = Action . runStateT $
(StateT $ unpackAction oldComp) >>= (StateT . unpackAction . f)
p@(Pure c) >>= f
| givesPure f = Pure . runState $
state oldF >>= (state . unpackPure . f)
| otherwise = liftComp p >>= f -- Just lift the first argument and recurse, to make an action
-- Helper functions used above:
unpackAction :: Computation a -> (CState -> IO (a, CState))
unpackAction (Pure c) = return . c
unpackAction (Action c) = c
-- Make an Action out of a Pure
liftComp :: Computation a -> Computation a
liftComp (Pure c) = Action $ return . c
liftComp a@(Action _) = a
所以唯一缺少的部分是givesPure
函數,我不確定它是否甚至可以實現它。 我曾經有過這樣的實現:
givesPure :: (a -> Computation b) -> Bool
givesPure f = isPure $ f undefined -- I actually used error with a custom message instead of undefined, but that shouldn't matter
isPure :: Computation a -> Bool
isPure (Pure _) = True
isPure (Action _) = False
這有效,但假設我綁定的函數總是返回具有相同純度的Computation,無論其輸入是什么。 這個假設合理地出現在我身上,因為計算的純度應該清楚地說明並且不依賴於某些計算,直到我注意到以下形式的函數不能用於這個假設:
baz :: Int -> Computation b
baz x = if x > 5
then foo
else bar
-- foo and bar both have the type Computation b
所以在我看來,就像它不可能這樣做,因為我需要當前的狀態來應用第一個Computation,以獲得函數的正確輸入以獲得第二個Computation並測試它是否是純的。
有沒有人看到這個解決方案或有證據表明它是不可能的?
您遇到過monadic計算不適合靜態分析的事實,因為效果(在您的情況下,效果的存在)取決於計算本身期間獲得的值。 如果不運行計算,則無法預測它們。
當你從Applicative
到Arrow
再到Monad
,你獲得了“力量”(你可以表達更多的計算),但卻失去了靜態分析的能力。
對於Applicative
,有一個現成的Lift
數據類型,它為現有的applicative添加了一個純計算。 但它沒有Monad
實例。
您可以嘗試使用GADT:
data Pure
data Action
data Computation t a where
Pure :: (CState -> (a, CState)) -> Computation t a
Action :: (CState -> IO (a, CState)) -> Computation Action a
這個想法是值x :: Computation Action a
可以做IO(但也可以是純粹的),而值y :: Computation Pure a
不能做IO。
例如,
liftComp :: Computation t a -> Computation Action a
liftComp (Pure c) = Pure c
liftComp x@(Action c) = x
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.