[英]How can I collect a list of ST actions inside Either?
如何將[Either ST]
轉換為Either [ST]
並隨后依次運行動作? 下面的代碼似乎適用於運行ST操作列表,但是當嘗試在Either中生成操作列表時(交換下面的test
定義),類型不再排列。 我本以為列表的類型是相同的,所以我非常感謝對這些差異的任何解釋。
{-# LANGUAGE RankNTypes #-}
import qualified Data.STRef as ST
import qualified Control.Monad.ST as ST
run :: (forall s. [ST.STRef s Int -> ST.ST s Int]) -> Int
run fs =
ST.runST $ do
x <- ST.newSTRef 0
mapM_ (\f ->
f x >>= ST.writeSTRef x
) fs
ST.readSTRef x
action :: ST.STRef s Int -> ST.ST s Int
action = \x -> ST.readSTRef x >>= \y -> return (y + 1)
-- test :: Either String Int
-- test = mapM (const (return action)) [0..5] >>= \fs -> return (run fs)
test :: Int
test = run (map (const action) [0..5])
main = print test
存在更高級別類型的類型推斷是不可判定的。 GHC的類型推理算法做了一些簡化的假設,這使得它不完整。 其中包括:
GHC將假設由lambda綁定的變量是一個單一型(它不包含(領先?) forall
s)
當您使用多態函數時,GHC將假定其類型中的自由類型變量將在monotypes中實例化
如果您禮貌地要求GHC使用特定的多元型,則可以覆蓋這兩個假設。
現在,您如何期望您的程序鍵入檢查?
run fs
鍵入check,我們最好有fs :: forall s. [ST.STRef s Int -> ST.ST s Int]
fs :: forall s. [ST.STRef s Int -> ST.ST s Int]
\\(fs :: forall s. [ST.STRef s Int -> ST.ST s Int]) -> ...
(使用ScopedTypeVariables
) 現在考慮使用>>=
。 它的類型是Monad m => ma -> (a -> mb) -> mb
。 但是,我們需要a = forall s. [ST.STRef s Int -> ST.ST s Int]
a = forall s. [ST.STRef s Int -> ST.ST s Int]
。 所以,根據上面的第二點,我們需要給出這個>>=
一個類型簽名,如
... `op` (\\(fs :: forall s. [ST.STRef s Int -> ST.ST s Int]) -> ...) where op :: Monad m => m (forall s. [ST.STRef s Int -> ST.ST s Int]) -> ((forall s. [ST.STRef s Int -> ST.ST s Int]) -> mb) -> mb op = (>>=)
op
應用程序中,第一個參數的類型為Either String (forall s. [ST.STRef s Int -> ST.ST s Int])
。 除非打開(損壞的)ImpredicativeTypes,否則不允許將類型構造函數(除(->)
)應用於多邊形類型。 但是,我們可以打開並繼續...... return :: Monad m => a -> ma
在a = forall s. ST.STRef s Int -> ST.ST s Int
實例化a = forall s. ST.STRef s Int -> ST.ST s Int
a = forall s. ST.STRef s Int -> ST.ST s Int
。 因此,我們需要添加另一個類型簽名來return
b = forall s. ST.STRef s Int -> ST.ST s Int
處實例化mapM :: Monad m => (a -> mb) -> [a] -> m [b]
b = forall s. ST.STRef s Int -> ST.ST s Int
b = forall s. ST.STRef s Int -> ST.ST s Int
如果你正在密切關注,你會發現另一個問題: mapM
的結果有類型
Either String [forall s. ST.STRef s Int -> ST.ST s Int]
但是(>>=)
的參數需要是類型的
Either String (forall s. [ST.STRef s Int -> ST.ST s Int])
你需要在這些之間進行轉換。 實際上這是一個無操作,但GHC並不夠聰明,所以你必須進行線性時間轉換,比如
liftM (\\x -> map (\\(y :: forall s. ST.STRef s Int -> ST.ST s Int) -> y) x)
(除了liftM
將需要另一種類型的簽名)
故事的道德:你可以這樣做,但你不應該這樣做。
通常你將有一個更簡單的時間,如果你隱藏你forall
的內線newtypes像
newtype S s = S { unS :: forall s. ST.STRef s Int -> ST.ST s Int }
這使得在程序中引入多態性的點更加明確(通過出現S
和unS
)。
您需要將函數包裝在newtype
以保留存在量化,而不必使用ImpredicativeTypes
。
{-# LANGUAGE RankNTypes #-}
import qualified Data.STRef as ST
import qualified Control.Monad.ST as ST
newtype STFunc = STFunc (forall s. ST.STRef s Int -> ST.ST s Int)
run :: [STFunc] -> Int
run fs = ST.runST $ do
x <- ST.newSTRef 0
mapM_ (\(STFunc f) ->
f x >>= ST.writeSTRef x
) fs
ST.readSTRef x
action :: STFunc
action = STFunc $ \x -> ST.readSTRef x >>= \y -> return (y + 1)
test :: Either String Int
test = mapM (const (return action)) [0..5] >>= \fs -> return (run fs)
main = print test
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.