簡體   English   中英

如何在Either中收集ST動作列表?

[英]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]
  • 因此,根據上面的第一點,我們必須在綁定它的lambda上寫下這種類型的簽名: \\(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 -> maa = 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 }

這使得在程序中引入多態性的點更加明確(通過出現SunS )。

您需要將函數包裝在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.

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