Skip to content

Commit 4398151

Browse files
authored
Merge pull request #473 from tweag/scope-with-movable
In scope-passing style: use a `Movable b` instead of `Ur b`
2 parents 964088a + 582cfda commit 4398151

File tree

6 files changed

+49
-30
lines changed

6 files changed

+49
-30
lines changed

src/Data/Array/Mutable/Linear/Internal.hs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ data Array a = Array (Array# a)
6262
-- | Allocate a constant array given a size and an initial value
6363
-- The size must be non-negative, otherwise this errors.
6464
alloc ::
65-
(HasCallStack) =>
65+
(HasCallStack, Movable b) =>
6666
Int ->
6767
a ->
68-
(Array a %1 -> Ur b) %1 ->
69-
Ur b
68+
(Array a %1 -> b) %1 ->
69+
b
7070
alloc s x f
7171
| s < 0 =
7272
(error ("Array.alloc: negative size: " ++ show s) :: x %1 -> x)
@@ -89,11 +89,11 @@ allocBeside s x (Array orig)
8989

9090
-- | Allocate an array from a list
9191
fromList ::
92-
(HasCallStack) =>
92+
(HasCallStack, Movable b) =>
9393
[a] ->
94-
(Array a %1 -> Ur b) %1 ->
95-
Ur b
96-
fromList list (f :: Array a %1 -> Ur b) =
94+
(Array a %1 -> b) %1 ->
95+
b
96+
fromList list (f :: Array a %1 -> b) =
9797
alloc
9898
(Prelude.length list)
9999
(error "invariant violation: unintialized array position")

src/Data/Array/Mutable/Unlifted/Linear.hs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,32 @@ infixr 0 `lseq` -- same fixity as base.seq
6060
-- | Allocate a mutable array of given size using a default value.
6161
--
6262
-- The size should be non-negative.
63-
alloc :: Int -> a -> (Array# a %1 -> Ur b) %1 -> Ur b
64-
alloc (GHC.I# s) a f =
63+
alloc :: (Movable b) => Int -> a -> (Array# a %1 -> b) %1 -> b
64+
alloc i a f = case move (unsafe_alloc i a f) of
65+
Ur b -> b
66+
{-# INLINEABLE alloc #-}
67+
68+
-- The `alloc` function is split in two. One very unsafe below (it's very
69+
-- unsafe, because `unafe_alloc 57 0 id` returns an unrestricted _mutable_
70+
-- `Array#` breaking the module's invariants). Because `unsafe_alloc` calls
71+
-- `runRW#`, it's marked as `NOINLINE`.
72+
--
73+
-- It's made safe by the wrapping function `alloc`, which restricts `b` to be
74+
-- `Movable` (`Array#` is crucially not `Movable`, therefore `alloc 57 0 id`
75+
-- doesn't type). Furthermore, `alloc` cases on `move` to make sure that all the
76+
-- effects have been run by the time we evaluate the result of an `alloc`. It's
77+
-- fine that `alloc` is inlined: its semantics is preserved by program
78+
-- transformations. It's useful that `alloc` be inlined, because in most
79+
-- instance `case move … of` will trigger a case-of-known-constructor avoiding
80+
-- an extra allocation. This is in particular the case for the common case where
81+
-- `b = Ur x`.
82+
unsafe_alloc :: Int -> a -> (Array# a %1 -> b) %1 -> b
83+
unsafe_alloc (GHC.I# s) a f =
6584
let new = GHC.runRW# Prelude.$ \st ->
6685
case GHC.newArray# s a st of
6786
(# _, arr #) -> Array# arr
6887
in f new
69-
{-# NOINLINE alloc #-} -- prevents runRW# from floating outwards
88+
{-# NOINLINE unsafe_alloc #-} -- prevents runRW# from floating outwards
7089

7190
-- For the reasoning behind these NOINLINE pragmas, see the discussion at:
7291
-- https://github.com/tweag/linear-base/pull/187#pullrequestreview-489183531

src/Data/HashMap/Mutable/Linear/Internal.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ data ProbeResult k v where
133133
-- | Run a computation with an empty 'HashMap' with given capacity.
134134
empty ::
135135
forall k v b.
136-
(Keyed k) =>
136+
(Keyed k, Movable b) =>
137137
Int ->
138-
(HashMap k v %1 -> Ur b) %1 ->
139-
Ur b
138+
(HashMap k v %1 -> b) %1 ->
139+
b
140140
empty size scope =
141141
let cap = max 1 size
142142
in Array.alloc cap Nothing (\arr -> scope (HashMap 0 cap arr))
@@ -151,10 +151,10 @@ allocBeside size (HashMap s' c' arr) =
151151
-- | Run a computation with an 'HashMap' containing given key-value pairs.
152152
fromList ::
153153
forall k v b.
154-
(Keyed k) =>
154+
(Keyed k, Movable b) =>
155155
[(k, v)] ->
156-
(HashMap k v %1 -> Ur b) %1 ->
157-
Ur b
156+
(HashMap k v %1 -> b) %1 ->
157+
b
158158
fromList xs scope =
159159
let cap =
160160
max

src/Data/Set/Mutable/Linear/Internal.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ type Keyed a = Linear.Keyed a
2929
-- # Constructors and Mutators
3030
-------------------------------------------------------------------------------
3131

32-
empty :: (Keyed a) => Int -> (Set a %1 -> Ur b) %1 -> Ur b
33-
empty s (f :: Set a %1 -> Ur b) =
32+
empty :: (Keyed a, Movable b) => Int -> (Set a %1 -> b) %1 -> b
33+
empty s (f :: Set a %1 -> b) =
3434
Linear.empty s (\hm -> f (Set hm))
3535

3636
toList :: (Keyed a) => Set a %1 -> Ur [a]
@@ -63,7 +63,7 @@ member :: (Keyed a) => a -> Set a %1 -> (Ur Bool, Set a)
6363
member a (Set hm) =
6464
Linear.member a hm Linear.& \(b, hm') -> (b, Set hm')
6565

66-
fromList :: (Keyed a) => [a] -> (Set a %1 -> Ur b) %1 -> Ur b
66+
fromList :: (Keyed a, Movable b) => [a] -> (Set a %1 -> b) %1 -> b
6767
fromList xs f =
6868
Linear.fromList (Prelude.map (,()) xs) (\hm -> f (Set hm))
6969

src/Data/Vector/Mutable/Linear/Internal.hs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,25 @@ fromArray arr =
5656
& \(Ur size', arr') -> Vec size' arr'
5757

5858
-- Allocate an empty vector
59-
empty :: (Vector a %1 -> Ur b) %1 -> Ur b
59+
empty :: (Movable b) => (Vector a %1 -> b) %1 -> b
6060
empty f = Array.fromList [] (f . fromArray)
6161

6262
-- | Allocate a constant vector of a given non-negative size (and error on a
6363
-- bad size)
6464
constant ::
65-
(HasCallStack) =>
65+
(HasCallStack, Movable b) =>
6666
Int ->
6767
a ->
68-
(Vector a %1 -> Ur b) %1 ->
69-
Ur b
68+
(Vector a %1 -> b) %1 ->
69+
b
7070
constant size' x f
7171
| size' < 0 =
7272
(error ("Trying to construct a vector of size " ++ show size') :: x %1 -> x)
7373
(f undefined)
7474
| otherwise = Array.alloc size' x (f . fromArray)
7575

7676
-- | Allocator from a list
77-
fromList :: (HasCallStack) => [a] -> (Vector a %1 -> Ur b) %1 -> Ur b
77+
fromList :: (HasCallStack, Movable b) => [a] -> (Vector a %1 -> b) %1 -> b
7878
fromList xs f = Array.fromList xs (f . fromArray)
7979

8080
-- | Number of elements inside the vector.

src/Foreign/Marshal/Pure/Internal.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ import Foreign.Marshal.Utils
2929
import Foreign.Ptr
3030
import Foreign.Storable
3131
import Foreign.Storable.Tuple ()
32-
import Prelude.Linear hiding (Eq (..), ($))
32+
import Prelude.Linear hiding (Eq (..))
3333
import System.IO.Unsafe
3434
import qualified Unsafe.Linear as Unsafe
35-
import Prelude (Eq (..), return, ($), (<$>), (<*>), (=<<))
35+
import Prelude (Eq (..), return, (<$>), (<*>), (=<<))
3636

3737
-- XXX: [2018-02-09] I'm having trouble with the `constraints` package (it seems
3838
-- that the version of Type.Reflection.Unsafe in the linear ghc compiler is not
@@ -290,20 +290,20 @@ freeAll start end = do
290290
-- TODO: document individual functions
291291

292292
-- | Given a linear computation that manages memory, run that computation.
293-
withPool :: (Pool %1 -> Ur b) %1 -> Ur b
294-
withPool scope = Unsafe.toLinear performScope scope
293+
withPool :: forall b. (Movable b) => (Pool %1 -> b) %1 -> b
294+
withPool scope = unur $ Unsafe.toLinear performScope scope
295295
where
296296
-- XXX: do ^ without `toLinear` by using linear IO
297297

298-
performScope :: (Pool %1 -> Ur b) -> Ur b
298+
performScope :: (Pool %1 -> b) -> Ur b
299299
performScope scope' = unsafeDupablePerformIO $ do
300300
-- Initialise the pool
301301
backPtr <- malloc
302302
let end = DLL backPtr nullPtr nullPtr -- always at the end of the list
303303
start <- DLL nullPtr nullPtr <$> new end -- always at the start of the list
304304
poke backPtr start
305305
-- Run the computation
306-
evaluate (scope' (Pool start))
306+
evaluate (move $ scope' (Pool start))
307307
`finally`
308308
-- Clean up remaining variables.
309309
(freeAll start end)

0 commit comments

Comments
 (0)