Add a constant time 'freeze' function to mutable arrays and vectors#205
Add a constant time 'freeze' function to mutable arrays and vectors#205
Conversation
aspiwack
left a comment
There was a problem hiding this comment.
You say that you have freezing primitives to mutable vectors (freeze would be a rather bad name for them, maybe share?) but I don't see any. It's fine, we don't need them yet, I can't say that I have a use case for such, so it can wait after the first release. You can make an issue if you think they may be important.
Let's not worry about thaw for the time being (I don't agree with your assessment that it can't be done in O(1) though, but we can discuss it elsewhere).
I have a slight change in interface in mind below, let's consider it.
| (# _, ret #) -> ret : go (i Prelude.+ 1) len arr | ||
|
|
||
| -- | /O(1)/ Convert an 'Array#' to an immutable 'Vector' (from 'vector' package). | ||
| freeze :: forall a. Array# a #-> Ur (Vector.Vector a) |
There was a problem hiding this comment.
I think it would have been natural, here, to convert to an unlifted immutable array. And post-convert to Vector in the unlifted case instead.
There was a problem hiding this comment.
MVector's constructor is as follows:
data MVector s a = MVector {-# UNPACK #-} !Int
{-# UNPACK #-} !Int
{-# UNPACK #-} !(MutableArray s a)
deriving ( Typeable )That MutableArray is from the primitive package:
-- | Mutable boxed arrays associated with a primitive state token.
data MutableArray s a = MutableArray
{ marray# :: MutableArray# s a }
deriving ( Typeable )So, that forces us to go through MutableArray. The Vector constructor work as you mentioned:
data Vector a = Vector {-# UNPACK #-} !Int
{-# UNPACK #-} !Int
{-# UNPACK #-} !(Array a)
deriving ( Typeable )
And it'd be ideal to use it, however that constructor is not exported.
There was a problem hiding this comment.
This is quite unfortunate indeed. It's the one type in vector which isn't exported.
Let me formulate a plan: do freeze to Array# here. To implement the conversion Array# -> Vector, use unsafeThaw# together with your present implementation based on MVector. But move that code to the lifted array module.
Then, next week, let us propose some kind of PR to the vector library so that we can use the Vector constructor directly in the future.
There was a problem hiding this comment.
I now understand why you are asking for this, and I agree. Freezing an unlifted mutable array to an unlifted immutable array makes sense. I did the change.
Which caused unlifted arrays freeze function to have a different signature:
freeze :: (GHC.Array# a -> b) -> Array# a #-> Ur bBecause I couldn't put an unlifted type inside a Ur. We are using the same pattern on unArray# function too, so I think it is fine.
There was a problem hiding this comment.
Yes, it's safe. Ur a is isomorphic to forall k. (a -> k) #-> k (exercise!).
Ultimately, we probably want to expose an unlifted unrestricted thing. There is no obstacle except time and syntax.
There was a problem hiding this comment.
Here is the corresponding issue, by the way: https://gitlab.haskell.org/ghc/ghc/-/issues/18490 .
It's here: linear-base/src/Data/Vector/Mutable/Linear.hs Lines 279 to 283 in 2963e17
Why do you think But I'm happy to rename it to |
|
You misread me. But I also misread you. So let's drop this line of conversation altogether. |
| -- We only need to do above because 'Vector' constructor is hidden. | ||
| -- Once it is exposed, we should be able to replace it with something | ||
| -- safer like: `go arr = Vector 0 (sizeof arr) arr` |
There was a problem hiding this comment.
Let's make an issue in our tracker to fix this. And we'll contribute upstream some way. After milestone 1.
Divesh-Otwani
left a comment
There was a problem hiding this comment.
This is really cool 😃
| where | ||
| go arr = unsafeDupablePerformIO $ do | ||
| mut <- Prim.unsafeThawArray (Prim.Array arr) | ||
| let mv = MVector.MVector 0 (Prim.sizeofMutableArray mut) mut |
There was a problem hiding this comment.
I think this is correct because of the source of the MVector instance for MVector.
c4fec0e to
7675496
Compare
As suggested in #180 (comment), this PR adds a constant time
freezemethod to unlifted and lifted mutable arrays and mutable vectors.As usual, it is implemented with unsafe primitives in unlifted arrays, and the rest of the collections make use of that implementation. I hope I got the
MVector's parameters right, as far as I could understand its first parameter is the starting index and the second index is the length. Because of that starting index, we can not have a constant timethawoperation (which would be unsafe anyway).I am not entirely sure about the name
freeze. I was also thinking of names liketoVectorortoImmutableVector, but they are likelier to be confused with either of ourVector's. Let me know if you prefer something else.Some simple tests pass, and I hope I'm not violating any invariant of the
vectorpackage.Relevant: #185