@@ -28,6 +28,7 @@ module Fleet.Array
2828 , toList
2929 , (!)
3030 , index
31+ , tag
3132 , set
3233 , copy
3334 , swap
@@ -36,7 +37,6 @@ module Fleet.Array
3637
3738import Prelude hiding (replicate )
3839
39- import Data.Tuple (Solo (MkSolo ))
4040import GHC.Exts hiding (fromList , toList , Lifted )
4141
4242import Data.Kind (Type )
@@ -157,25 +157,55 @@ A v0 ! i0 = unsafeDupablePerformIO (go v0 i0) where
157157 | i == j2 -> go v' j1
158158 | otherwise -> go v' i
159159
160+ data Token = Token (State # RealWorld )
161+
162+ returnToken :: a -> IO (a , Token )
163+ returnToken x = IO (\ s -> (# s , (x, Token s) # ))
164+
160165-- | Indexing an array. O(1)
161- -- Using the 'Solo' constructor, you can sequence indexing to happen before
162- -- future updates without having to evaluate the element itself.
166+ --
167+ -- The tuple and 'Token' serve two purposes:
168+ --
169+ -- - You can now separately force the evaluation of the tuple and the actual
170+ -- array element
171+ -- - You can use the 'Token' to with the 'tag' function on an array to force
172+ -- the indexing to happen before the array can be written to.
163173{-# INLINE index #-}
164- index :: Int -> Array a -> Solo a
174+ index :: Int -> Array a -> ( a , Token )
165175index i0 (A v0) = unsafeDupablePerformIO (go v0 i0) where
166176 go v i = do
167177 dat <- readMutVar v
168178 case dat of
169- Current arr -> MkSolo <$> readMutArray arr i
179+ Current arr -> readMutArray arr i >>= returnToken
170180 -- _ -> error "Accessing old version"
171181 Diff (Set j x) xs
172- | i == j -> pure ( MkSolo x)
182+ | i == j -> returnToken x
173183 | otherwise -> go xs i
174184 Diff (Swap j1 j2) xs
175185 | i == j1 -> go xs j2
176186 | i == j2 -> go xs j1
177187 | otherwise -> go xs i
178188
189+ -- | This is a no-op, but can be used to enforce an ordering between indexing
190+ -- and other array operations, to avoid the overhead of indexing from older
191+ -- versions of the array.
192+ --
193+ -- For example, swapping two elements in an array by using 'index'
194+ -- and 'set' can be done like this:
195+ --
196+ -- @
197+ -- swap :: Int -> Int -> Array a -> Array a
198+ -- swap i j xs =
199+ -- let (x, t1) = index i xs
200+ -- (y, t2) = index j xs
201+ -- in set i y (set j x (tag t1 (tag t2 xs)))
202+ -- @
203+ --
204+ -- This ensures the indexing happens before the setting.
205+ {-# NOINLINE tag #-}
206+ tag :: Token -> Array a -> Array a
207+ tag (Token _) xs = xs
208+
179209{-# INLINE invert #-}
180210invert :: MutArray a -> Op a -> IO (Op a )
181211invert _ (Swap i j) = pure (Swap i j)
0 commit comments