Skip to content
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

A desirable Boxed instance for Unboxed #508

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

recursion-ninja
Copy link
Contributor

This PR contains a proof of concept for Unbox instances of "boxed" data-types by way of the newly added AsBoxedStrictly and AsBoxedLazily newtypes. This PR is intended to address issue #503.

The AsBoxedStrictly newtype imposes new strictness semantics on the underlying type, ensuring the wrapped value is always reduced to normal form. This invariant requires an NFData instance for the underlying type. Furthermore, it requires the use of a "smart constructor" which evaluates the wrapped value to normal form, since a new-type cannot have strictness annotations. Hence there are makeBoxedStrictly and grabBoxedStrictly exposed from the Data.Vector.Unboxed module to construct and deconstruct AsBoxedStrictly values, respectively.

The AsBoxedLazily new-type is much simpler since it preserves the strictness semantics of the underlying data-type unaltered.

Here are two example usages of the newtypes as a proof of concept:

Strict usage

data Foo a = Foo Int a
  deriving (Eq, Ord, Show)

instance NFData a => VU.IsoUnbox (Foo a) (Int, AsBoxedStrictly a) where
  toURepr (Foo i a) = (i, makeBoxedStrictly a)
  fromURepr (i, a) = Foo i $ grabBoxedStrictly a
  {-# INLINE toURepr #-}
  {-# INLINE fromURepr #-}
  
newtype instance VU.MVector s (Foo a) = MV_Foo (VU.MVector s (Int, AsBoxedStrictly a))

newtype instance VU.Vector    (Foo a) = V_Foo  (VU.Vector    (Int, AsBoxedStrictly a))

deriving via (Foo a `VU.As` (Int, AsBoxedStrictly a)) instance NFData a => VGM.MVector VUM.MVector (Foo a)

deriving via (Foo a `VU.As` (Int, AsBoxedStrictly a)) instance NFData a => VG.Vector   VU.Vector   (Foo a)

instance NFData a => VU.Unbox (Foo a)

In GHCi

import qualified Data.Vector.Unboxed as VU
VU.fromListN 3 [ Foo 4 "Hello", Foo 8 "there", Foo 16 "sailor" ]
[Foo 4 "Hello",Foo 8 "there",Foo 16 "sailor"]

Lazy usage

data Bar a = Bar Int a
  deriving (Eq, Ord, Show)

instance VU.IsoUnbox (Bar a) (Int, AsBoxedLazily a) where
  toURepr (Bar i a) = (i, AsBoxedLazily a)
  fromURepr (i, AsBoxedLazily a) = Bar i a
  {-# INLINE toURepr #-}
  {-# INLINE fromURepr #-}
  
newtype instance VU.MVector s (Bar a) = MV_Bar (VU.MVector s (Int, AsBoxedLazily a))

newtype instance VU.Vector    (Bar a) = V_Bar  (VU.Vector    (Int, AsBoxedLazily a))

deriving via (Bar a `VU.As` (Int, AsBoxedLazily a)) instance VGM.MVector VUM.MVector (Bar a)

deriving via (Bar a `VU.As` (Int, AsBoxedLazily a)) instance VG.Vector   VU.Vector   (Bar a)

instance VU.Unbox (Bar a)

In GHCi

import qualified Data.Vector.Unboxed as VU
VU.fromListN 3 [ Bar 3 "Bye", Bar 2 "for", Bar 1 "now" ]
[Bar 3 "Bye",Bar 2 "for",Bar 1 "now"]

Nota Bene

Since this is a proof of concept, all types and functions names are subject to change after the appropriate level of "bike-shedding" has occurred. Commentary is welcomed and encouraged.

@Shimuuar
Copy link
Contributor

I haven't looked at the code yet. CI is broken at the moment fix should be in #507

Copy link
Contributor

@Shimuuar Shimuuar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rebase on top current mater. CI is fixed there.

For strict and lazy vectors you simply need to pick Data.Vector.Strict/Data.Vector as a representation and coerce their methods same way as UnboxViaPrim do,

Variant which reduces values to NF is slightly more complicated. You can pick either strict or lazy vector as a representation. Former seems more logical. We evaluate value whenever it's stored in a vector. So following methods need custom code: basicUnsafeWrite, basicUnsafeReplicate, elemseq

Default implementation of baiscSet for boxed vectors basicSet is fine, it's defined in terms of basicUnsafeWrite

-- | Smart constructor for creating values of the 'AsBoxedStrictly' newtype.
-- The wrapped values are evaluated to normal form via thier 'NFData' instance.
--
-- @since 0.13.2.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason to add constructor/accessor. Newtype's constructor should be exported anyway in order for deriving via to work.

Anyway we aren't ensuring strictness in wrapper newtype. We're doing it in type class methods. Newtype is going to be coerced away. It's only used to pick instance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants