-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add unsafeCast
to Primitive
vectors
#401
Conversation
c1e1950
to
24d64b0
Compare
Or unsafeCast over newtypes for that matter |
# Changes in version 0.13.0.0 * `mkType` from `Data.Vector.Generic` is deprecated in favor of `Data.Data.mkNoRepType` * The role signatures on several `Vector` types were too permissive, so they have been tightened up: * The role signature for `Data.Vector.Mutable.MVector` is now `type role MVector nominal representational` (previously, both arguments were `phantom`). [#224](haskell/vector#224) * The role signature for `Data.Vector.Primitive.Vector` is now `type role Vector nominal` (previously, it was `phantom`). The role signature for `Data.Vector.Primitive.Mutable.MVector` is now `type role MVector nominal nominal` (previously, both arguments were `phantom`). [#316](haskell/vector#316) * The role signature for `Data.Vector.Storable.Vector` is now `type role Vector nominal` (previous, it was `phantom`), and the signature for `Data.Vector.Storable.Mutable.MVector` is now `type role MVector nominal nominal` (previous, both arguments were `phantom`). [#235](haskell/vector#235) We pick `nominal` for the role of the last argument instead of `representational` since the internal structure of a `Storable` vector is determined by the `Storable` instance of the element type, and it is not guaranteed that the `Storable` instances between two representationally equal types will preserve this internal structure. One consequence of this choice is that it is no longer possible to `coerce` between `Storable.Vector a` and `Storable.Vector b` if `a` and `b` are nominally distinct but representationally equal types. We now provide `unsafeCoerce{M}Vector` and `unsafeCast` functions to allow this (the onus is on the user to ensure that no `Storable` invariants are broken when using these functions). * Methods of type classes `Data.Vector.Generic.Mutable.MVector` and `Data.Vector.Generic.Vector` use concrete monads (`ST`, etc) istead of being polymorphic (`PrimMonad`, etc). [#335](haskell/vector#335). This makes it possible to derive `Unbox` with: * `GeneralizedNewtypeDeriving` * via `UnboxViaPrim` and `Prim` instance * via `As` and `IsoUnbox` instance: [#378](haskell/vector#378) * Add `MonadFix` instance for boxed vectors: [#312](haskell/vector#312) * Re-export `PrimMonad` and `RealWorld` from mutable vectors: [#320](haskell/vector#320) * Add `maximumOn` and `minimumOn`: [#356](haskell/vector#356) * The functions `scanl1`, `scanl1'`, `scanr1`, and `scanr1'` for immutable vectors are now defined when given empty vectors as arguments, in which case they return empty vectors. This new behavior is consistent with the one of the corresponding functions in `Data.List`. Prior to this change, applying an empty vector to any of those functions resulted in an error. This change was introduced in: [#382](haskell/vector#382) * Change allocation strategy for `unfoldrN`: [#387](haskell/vector#387) * Remove `CPP` driven error reporting in favor of `HasCallStack`: [#397](haskell/vector#397) * Remove redundant `Storable` constraints on to/from `ForeignPtr` conversions: [#394](haskell/vector#394) * Add `unsafeCast` to `Primitive` vectors: [#401](haskell/vector#401) * Make `(!?)` operator strict: [#402](haskell/vector#402) * Add `readMaybe`: [#425](haskell/vector#425) * Add `groupBy` and `group` for `Data.Vector.Generic` and the specialized version in `Data.Vector`, `Data.Vector.Unboxed`, `Data.Vector.Storable` and `Data.Vector.Primitive`. [#427](haskell/vector#427) * Add `toArraySlice` and `unsafeFromArraySlice` functions for conversion to and from the underlying boxed `Array`: [#434](haskell/vector#434)
Why not? |
@qrpnxz Because unlike Storable vector we cannot start at an arbitrary byte offset, we have to think in element offsets. Unfortunately it is a limitation of For example let's try to cast a primitive w16vec :: Vector Word16
w16vec = drop 3 $ fromList [1,2,3,4,5,6,7,8] when we drop 3 elements we do a O(1) slice, which makes w16vec :: Vector Word16
w16vec = Vector 3 5 (ba :: ByteArray)
where ba = fromList [1,2,3,4,5,6,7,8] If we were to convert this vector using the same strategy as in Storable we'd expect a w32vec :: Vector Word64
w32vec = Vector ? 1 (ba :: ByteArray) Problem is that the offset must be specified in number of elements, and it must be specified in number of elements because |
How unfortunate. Thanks for explaining. |
A pinned indexByteOffPrim :: Prim a => ByteArray# -> Int# -> a
indexByteOffPrim arr# idx# =
case byteArrayContents# arr# of
addr# -> indexOffAddr# (plusAddr# addr# idx#) 0 writeByteOffPrim :: Prim a => MutableByteArray# -> Int# -> a -> State# s -> State# s
writeByteOffPrim marr# idx# a s0# =
case unsafeFreezeByteArray# marr# st0# of
(# s1#, arr# #) -> case byteArrayContents arr# of
addr# -> writeOffAddr# (plusAddr# arr# idx#) 0 a s1# EDIT: In the latest writeByteOffPrim :: Prim a => MutableByteArray# -> Int# -> a -> State# s -> State# s
writeByteOffPrim marr# idx# a s0# =
case mutableByteArrayContents marr# of
addr# -> writeOffAddr# (plusAddr# arr# idx#) 0 a s0# |
@qrpnxz Your pinned solution would not work for I know for certain that this problem is solvable with primitive operations that work on a byte offset, such as |
I don't see why it wouldn't work. Perhaps I should mention that the offset would now be in bytes. Offset recalculation would not even be needed at all in a cast; it would stay the same. As to why you would want a pinned version of the primitive vector, here is one use-case: you can zero-copy convert a pinned primitive vector into both Although the |
The whole point of Switching primitive vector to use byte offset is a serious breaking change, which could happen, but only if primitive supported it and there was enough motivation to get this done. FYI:
So yes, the only acceptable solution is to go and change |
Goodness, I did not mean to imply changing the current Primitive to pinned! (😄) I was talking about having pinned variant; that is, a whole other type. Of course we still want the unpinned one.
I said so myself? The plus is being able to do both Text and ByteString. (Useful in parsing.) |
Maintaining five different modules with almost the same API already takes a lot of work, I'd be dreading adding one more module to that list. It feels like we keep talking past each other. I recommend you open a ticket explaining clearly:
If all maintainers will agree that it really is something worthwhile implementing an maintaining then we can come up with the plan on how to get it done. This would also allow other from the community to pitch in with ideas. Discussing this on a merged PR does not make much sense and it doesn't have the same visibility. |
While reviewing #398 I noticed that there is no
unsafeCast
for primitive vector.We can't provide a cast that changes primitive vectors with elements of different sizes in the way that
Data.Vector.Storable.unsafeCast
does it. For exampleVector Word8
->Vector Word16
cast cannot be implemented with currentprimitive
. However, this doesn't mean a cast likeVector Word8
->Word Int8
or aVector Float
->Vector Word32
cannot be useful for some reason.