代码之家  ›  专栏  ›  技术社区  ›  n. m. could be an AI

分段可变状态的单子

  •  5
  • n. m. could be an AI  · 技术社区  · 6 年前

    我知道有多平凡 State 作品( 编辑:显然不是! ).

    如果我需要创建一个数组,并且不方便一次创建整个数组,我可以创建一个 STArray ,填充它,然后冻结并向用户返回一个普通的不可变数组。

    现在假设我需要同时创建两个不同类型的数组。

    更一般地说,我可能想创建一个具有可变节点的任意图,像修改 星光

    我不想诉诸法律 IOArray 或者其他什么 IO

    1 回复  |  直到 6 年前
        1
  •  5
  •   chi    6 年前

    这里有一些选择。

    {-# LANGUAGE RankNTypes #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    module TestSTArray where
    
    import Control.Monad.ST
    import Data.Array.ST
    import Data.Array
    import Data.Array.MArray
    
    -- not needed until later on    
    import GHC.Arr (unsafeFreezeSTArray)
    

    最基本、安全的方法是冷冻。不过,这会造成一份副本。

    test2Safe :: (Array Int Char, Array Int Bool)
    test2Safe = runST $ do
       a1 <- newArray (0,9) 'A' :: ST s (STArray s Int Char)
       a2 <- newArray (0,9) False :: ST s (STArray s Int Bool)
       writeArray a1 5 'B'
       x <- readArray a2 6
       writeArray a1 7 (if x then 'X' else 'Y')
       writeArray a2 5 True
       arr1 <- freeze a1
       arr2 <- freeze a2
       return (arr1, arr2)
    

    runSTArray . 这样我们就避免了抄袭。

    runSTArray2 :: (forall s. ST s (STArray s i1 e1, STArray s i2 e2))
                -> (Array i1 e1, Array i2 e2) 
    runSTArray2 st = runST $ do
      (a1, a2) <- st
      (,) <$> unsafeFreezeSTArray a1 <*> unsafeFreezeSTArray a2
    

    我相信上述使用 unsafe 因为我们不再使用 a1,a2 在不安全的冻结之后,所以不需要拷贝。

    当然,上面的包装器可以推广到更多的数组。可以说,应该在库中放一个更通用的版本。

    最后,我们可以利用辅助功能:

    test2LessSafe :: (Array Int Char, Array Int Bool)
    test2LessSafe = runSTArray2 $ do
       a1 <- newArray (0,9) 'A' :: ST s (STArray s Int Char)
       a2 <- newArray (0,9) False :: ST s (STArray s Int Bool)
       writeArray a1 5 'B'
       x <- readArray a2 6
       writeArray a1 7 (if x then 'X' else 'Y')
       writeArray a2 5 True
       return (a1, a2)