Skip to content

Commit d0863c6

Browse files
author
Divesh Otwani
authored
Merge pull request #287 from tweag/better-push-arrays-2
More generic push arrays 1/2
2 parents eef8e77 + e442c55 commit d0863c6

File tree

3 files changed

+95
-34
lines changed

3 files changed

+95
-34
lines changed

src/Data/Array/Destination.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ module Data.Array.Destination
123123
, mirror
124124
, fromFunction
125125
, fill
126+
, dropEmpty
126127
)
127128
where
128129

@@ -179,6 +180,14 @@ fill = Unsafe.toLinear2 unsafeFill
179180
else
180181
unsafeDupablePerformIO Prelude.$ MVector.write ds 0 a
181182

183+
-- | @dropEmpty dest@ consumes and empty array and fails otherwise.
184+
dropEmpty :: HasCallStack => DArray a %1-> ()
185+
dropEmpty = Unsafe.toLinear unsafeDrop where
186+
unsafeDrop :: DArray a -> ()
187+
unsafeDrop (DArray ds)
188+
| MVector.length ds > 0 = error "Destination.dropEmpty on non-empty array."
189+
| otherwise = ds `seq` ()
190+
182191
-- | @'split' n dest = (destl, destr)@ such as @destl@ has length @n@.
183192
--
184193
-- 'split' is total: if @n@ is larger than the length of @dest@, then

src/Data/Array/Polarized.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ module Data.Array.Polarized
9999
)
100100
where
101101

102-
import qualified Data.Array.Destination as DArray
103102
import qualified Data.Array.Polarized.Pull.Internal as Pull
104103
import qualified Data.Array.Polarized.Pull as Pull
105104
import qualified Data.Array.Polarized.Push as Push
105+
import qualified Data.Foldable as NonLinear
106106
import Prelude.Linear
107107
import Data.Vector (Vector)
108108

@@ -129,7 +129,8 @@ import Data.Vector (Vector)
129129
-- | Convert a pull array into a push array.
130130
-- NOTE: this does NOT require allocation and can be in-lined.
131131
transfer :: Pull.Array a %1-> Push.Array a
132-
transfer (Pull.Array f n) = Push.Array (\g -> DArray.fromFunction (\i -> g (f i))) n
132+
transfer (Pull.Array f n) =
133+
Push.Array (\k -> NonLinear.foldMap' (\x -> k (f x)) [0..(n-1)])
133134

134135
-- | This is a shortcut convenience function
135136
-- for @transfer . Pull.fromVector@.

src/Data/Array/Polarized/Push.hs

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
{-# OPTIONS_GHC -fno-warn-partial-type-signatures #-}
21
{-# LANGUAGE DerivingVia #-}
32
{-# LANGUAGE GADTs #-}
43
{-# LANGUAGE LinearTypes #-}
@@ -11,55 +10,107 @@
1110
-- allocated for an array. See @Data.Array.Polarized@.
1211
--
1312
-- This module is designed to be imported qualified as @Push@.
14-
module Data.Array.Polarized.Push where
13+
module Data.Array.Polarized.Push
14+
( Array(..)
15+
, alloc
16+
, make
17+
)
18+
where
1519

16-
-- XXX: it might be better to hide the data constructor, in case we wish to
17-
-- change the implementation.
18-
19-
import Data.Array.Destination (DArray)
20-
import qualified Data.Array.Destination as DArray
2120
import qualified Data.Functor.Linear as Data
21+
import qualified Data.Array.Destination as DArray
22+
import Data.Array.Destination (DArray)
2223
import Data.Vector (Vector)
23-
import Prelude.Linear
2424
import qualified Prelude
25+
import Prelude.Linear
26+
import GHC.Stack
27+
2528

26-
-- TODO: the below isn't really true yet since no friendly way of constructing
27-
-- a PushArray directly is given yet (see issue #62), but the spirit holds.
28-
-- TODO: There's also a slight lie in that one might want to consume a
29-
-- PushArray into a commutative monoid, for instance summing all the elements,
30-
-- and this is not yet possible, although it should be.
29+
-- The Types
30+
-------------------------------------------------------------------------------
3131

3232
-- | Push arrays are un-allocated finished arrays. These are finished
3333
-- computations passed along or enlarged until we are ready to allocate.
3434
data Array a where
35+
Array :: (forall m. Monoid m => (a -> m) -> m) %1-> Array a
36+
-- Developer notes:
37+
--
38+
-- Think of @(a -> m)@ as something that writes an @a@ and think of
39+
-- @((a -> m) -> m)@ as something that takes a way to write a single element
40+
-- and writes and concatenates all elements.
41+
--
42+
-- Also, note that in this formulation we don't know the length beforehand.
43+
44+
data ArrayWriter a where
45+
ArrayWriter :: (DArray a %1-> ()) %1-> !Int -> ArrayWriter a
3546
-- The second parameter is the length of the @DArray@
36-
Array :: (forall b. (a %1-> b) -> DArray b %1-> ()) %1-> Int -> Array a
37-
deriving Prelude.Semigroup via NonLinear (Array a)
3847

39-
instance Data.Functor Array where
40-
fmap f (Array k n) = Array (\g dest -> k (g . f) dest) n
4148

42-
instance Semigroup (Array a) where
43-
(<>) = append
49+
-- API
50+
-------------------------------------------------------------------------------
4451

45-
-- XXX: the use of Vector in the type of alloc is temporary (see also
46-
-- "Data.Array.Destination")
4752
-- | Convert a push array into a vector by allocating. This would be a common
4853
-- end to a computation using push and pull arrays.
4954
alloc :: Array a %1-> Vector a
50-
alloc (Array k n) = DArray.alloc n (k id)
55+
alloc (Array k) = allocArrayWriter $ k singletonWriter where
56+
singletonWriter :: a -> ArrayWriter a
57+
singletonWriter a = ArrayWriter (DArray.fill a) 1
58+
59+
allocArrayWriter :: ArrayWriter a %1-> Vector a
60+
allocArrayWriter (ArrayWriter writer len) = DArray.alloc len writer
5161

5262
-- | @`make` x n@ creates a constant push array of length @n@ in which every
5363
-- element is @x@.
54-
make :: a -> Int -> Array a
55-
make x n = Array (\k -> DArray.replicate (k x)) n
64+
make :: HasCallStack => a -> Int -> Array a
65+
make x n
66+
| n < 0 = error "Making a negative length push array"
67+
| otherwise = Array (\makeA -> mconcat $ Prelude.replicate n (makeA x))
68+
69+
70+
-- # Instances
71+
-------------------------------------------------------------------------------
72+
73+
instance Data.Functor Array where
74+
fmap f (Array k) = Array (\g -> k (\x -> (g (f x))))
75+
76+
instance Prelude.Semigroup (Array a) where
77+
(<>) x y = append x y
78+
79+
instance Semigroup (Array a) where
80+
(<>) = append
81+
82+
instance Prelude.Monoid (Array a) where
83+
mempty = empty
84+
85+
instance Monoid (Array a) where
86+
mempty = empty
87+
88+
empty :: Array a
89+
empty = Array (\_ -> mempty)
5690

57-
-- | Concatenate two push arrays.
5891
append :: Array a %1-> Array a %1-> Array a
59-
append (Array kl nl) (Array kr nr) =
60-
Array
61-
(\f dest -> parallelApply f kl kr (DArray.split nl dest))
62-
(nl+nr)
63-
where
64-
parallelApply :: (a %1-> b) -> ((a %1-> b) -> DArray b %1-> ()) %1-> ((a %1-> b) -> DArray b %1-> ()) %1-> (DArray b, DArray b) %1-> ()
65-
parallelApply f' kl' kr' (dl, dr) = kl' f' dl <> kr' f' dr
92+
append (Array k1) (Array k2) = Array (\writeA -> k1 writeA <> k2 writeA)
93+
94+
instance Prelude.Semigroup (ArrayWriter a) where
95+
(<>) x y = addWriters x y
96+
97+
instance Prelude.Monoid (ArrayWriter a) where
98+
mempty = emptyWriter
99+
100+
instance Semigroup (ArrayWriter a) where
101+
(<>) = addWriters
102+
103+
instance Monoid (ArrayWriter a) where
104+
mempty = emptyWriter
105+
106+
addWriters :: ArrayWriter a %1-> ArrayWriter a %1-> ArrayWriter a
107+
addWriters (ArrayWriter k1 l1) (ArrayWriter k2 l2) =
108+
ArrayWriter
109+
(\darr ->
110+
(DArray.split l1 darr) & \(darr1,darr2) -> consume (k1 darr1, k2 darr2))
111+
(l1+l2)
112+
113+
emptyWriter :: ArrayWriter a
114+
emptyWriter = ArrayWriter DArray.dropEmpty 0
115+
-- Remark. @emptyWriter@ assumes we can split a destination array at 0.
116+

0 commit comments

Comments
 (0)