@@ -21,13 +21,24 @@ the latest version of the array. Otherwise the performance regresses to O(k),
2121where k is the number of changes between the version you are accessing and the
2222latest version.
2323-}
24- module Fleet.Array (Array , fromList , toList , (!) , index , set , copy , swap , aseq ) where
24+ module Fleet.Array
25+ ( Array
26+ , fromList
27+ , toList
28+ , (!)
29+ , index
30+ , set
31+ , copy
32+ , swap
33+ , pseq
34+ ) where
2535
2636import Data.Tuple (Solo (MkSolo ))
2737import GHC.Exts hiding (fromList , toList , Lifted )
2838
2939import Data.Kind (Type )
3040import GHC.IO.Unsafe (unsafeDupablePerformIO )
41+ import GHC.Conc (pseq )
3142
3243import Fleet.Array.MutVar
3344import Fleet.Array.Lift
@@ -54,10 +65,6 @@ pattern Diff op v = Lift (Diff# op v)
5465instance Show a => Show (Array a ) where
5566 show xs = " fromList " ++ show (toList xs)
5667
57- -- | Sequencing array operations.
58- aseq :: a -> b -> b
59- aseq x y = x `seq` lazy y
60-
6168-- | Convert a list into an array. O(n)
6269fromList :: [a ] -> Array a
6370fromList xs = unsafeDupablePerformIO $ do
@@ -92,6 +99,34 @@ toList (A v) = unsafeDupablePerformIO $ do
9299 go 0
93100
94101-- | Indexing an array. O(1)
102+ --
103+ -- __WARNING:__ If you were to write your own 'swap' function. You might be
104+ -- tempted to write it like this:
105+ --
106+ -- > swap :: Int -> Int -> Array a -> Array a
107+ -- > swap !i !j !xs = set i (xs ! j) (set j (xs ! i) xs)
108+ --
109+ -- Unfortunately, this leaves the order between the reads and writes undefined.
110+ -- And in practice, GHC picks the wrong order. To enforce that reads happen
111+ -- before writes, you can use 'pseq' like this:
112+ --
113+ -- > swap !i !j !xs =
114+ -- > let
115+ -- > x = xs ! i
116+ -- > y = xs ! j
117+ -- > in x `pseq` y `pseq` set i y (set j x xs)
118+ --
119+ -- If you want to avoid forcing the elements in the array, then you can use
120+ -- 'index' like this:
121+ --
122+ -- > swap !i !j !xs =
123+ -- > let
124+ -- > x = index i xs
125+ -- > y = index j xs
126+ -- > in x `pseq` y `pseq` set i (getSolo y) (set j (getSolo x) xs)
127+ --
128+ -- In the future, we hope to write a GHC plugin that can automatically detect
129+ -- when pseq is necessary in common cases.
95130{-# INLINE (!) #-}
96131(!) :: Array a -> Int -> a
97132A v0 ! i0 = unsafeDupablePerformIO (go v0 i0) where
@@ -141,27 +176,28 @@ appOp arr (Swap i j) = do
141176 writeMutArray arr i y
142177 writeMutArray arr j x
143178
144- {-# INLINE appDiffOp #-}
145- appDiffOp :: Op a -> Array a -> Array a
146- appDiffOp op ( A v) = unsafeDupablePerformIO $ do
179+ {-# INLINE reversePointers #-}
180+ reversePointers :: ArrayVar a -> IO ( MutArray a )
181+ reversePointers v = do
147182 dat <- readMutVar v
148183 case dat of
149- xs@ (Current arr) -> do
184+ Current arr -> pure arr
185+ Diff op v' -> do
186+ arr <- reversePointers v'
150187 op' <- invert arr op
151188 appOp arr op
152- v' <- newMutVar xs
153- writeMutVar v (Diff op' v')
154- pure (A v')
155- Diff op' v' -> do
156- -- TODO: pointer inversion instead of copy
157- -- first invert all pointers until Current
158- -- then apply all updates until back at v
159- -- then do the same as above
160- arr <- copyInternal v'
161- appOp arr op'
162- appOp arr op
163- v'' <- newMutVar (Current arr)
164- pure (A v'')
189+ writeMutVar v' (Diff op' v)
190+ pure arr
191+
192+ {-# INLINE appDiffOp #-}
193+ appDiffOp :: Op a -> Array a -> Array a
194+ appDiffOp op (A v) = unsafeDupablePerformIO $ do
195+ arr <- reversePointers v
196+ op' <- invert arr op
197+ appOp arr op
198+ v' <- newMutVar (Current arr)
199+ writeMutVar v (Diff op' v')
200+ pure (A v')
165201
166202-- | Update the array element at a given position to a new value. O(1)
167203{-# INLINE set #-}
0 commit comments