unboxing-vector 0.1.0.0 → 0.1.1.0
raw patch · 17 files changed
+527/−106 lines, 17 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Vector.Unboxing: Enum :: a -> Enum a
+ Data.Vector.Unboxing: EnumRep :: a -> EnumRep rep a
+ Data.Vector.Unboxing: newtype Enum a
+ Data.Vector.Unboxing: newtype EnumRep rep a
+ Data.Vector.Unboxing.Mutable: Enum :: a -> Enum a
+ Data.Vector.Unboxing.Mutable: EnumRep :: a -> EnumRep rep a
+ Data.Vector.Unboxing.Mutable: newtype Enum a
+ Data.Vector.Unboxing.Mutable: newtype EnumRep rep a
Files
- ChangeLog.md +6/−0
- README.md +45/−10
- src/Data/Vector/Unboxing.hs +6/−3
- src/Data/Vector/Unboxing/Internal.hs +165/−28
- src/Data/Vector/Unboxing/Mutable.hs +2/−0
- test-deriving-via/Enum.hs +13/−0
- test-deriving-via/Foo.hs +66/−0
- test-deriving-via/Generic.hs +34/−0
- test-deriving-via/Spec.hs +11/−0
- test-deriving-via/TestTypeErrors.hs +19/−0
- test-gnd/Foo.hs +17/−0
- test-gnd/Spec.hs +56/−0
- test/Generic.hs +0/−33
- test/Loop.hs +33/−0
- test/Spec.hs +0/−19
- test/TestTypeErrors.hs +0/−9
- unboxing-vector.cabal +54/−4
ChangeLog.md view
@@ -1,5 +1,11 @@ # Changelog for unboxing-vector +## Version 0.1.1.0 (2019-07-01)++- Support older GHC versions.+- Add `Enum` and `EnumRep` wrappers.+- Add `Unboxable` instance for `Ordering`.+ ## Version 0.1.0.0 (2019-06-17) Initial release with
README.md view
@@ -1,5 +1,7 @@-# unboxing-vector+# unboxing-vector: A newtype-friendly variant of unboxed vectors +[](https://travis-ci.org/minoki/unboxing-vector)+ This package provides newtype-friendly wrappers for `Data.Vector.Unboxed` in [`vector` package](http://hackage.haskell.org/package/vector). ## Description@@ -7,48 +9,69 @@ Suppose you define a newtype for `Int` and want to store them in an unboxed vector. ```haskell+import qualified Data.Vector.Unboxed as Unboxed+ newtype Foo = Foo Int -generate 10 (\i -> Foo i) :: Data.Vector.Unboxed.Vector Foo+vec :: Unboxed.Vector Foo+vec = Unboxed.generate 10 (\i -> Foo i) ``` -With plain `Data.Vector.Unboxed`, you either write two dozen of lines of code to get it work (the exact code is [here](https://github.com/minoki/unboxing-vector/blob/3a152014b9660ef1e2885d6b9c66423064223f63/test/Foo.hs#L36-L63)), or resort to Template Haskell ([`vector-th-unbox` package](http://hackage.haskell.org/package/vector-th-unbox)) to generate it.+With classic `Data.Vector.Unboxed`, you either write _two dozen of lines_ of code to get it work (the exact code is [here](https://github.com/minoki/unboxing-vector/blob/3a152014b9660ef1e2885d6b9c66423064223f63/test/Foo.hs#L36-L63)), or resort to Template Haskell ([`vector-th-unbox` package](http://hackage.haskell.org/package/vector-th-unbox)) to generate it. -But with `Data.Vector.Unboxing`, the code you write is just two lines:+Now you have the third option, namely `Data.Vector.Unboxing`.+With `Data.Vector.Unboxing`, the amount of code you write is just _two lines_: ```haskell-instance Data.Vector.Unboxing.Unboxable Foo where+import qualified Data.Vector.Unboxing as Unboxing++instance Unboxing.Unboxable Foo where type Rep Foo = Int -generate 10 (\i -> Foo i) :: Data.Vector.Unboxing.Vector Foo+vec :: Unboxing.Vector Foo+vec = Unboxing.generate 10 (\i -> Foo i) ``` ...and if you want to be even more concise, you can derive `Unboxable` instance with `GeneralizedNewtypeDeriving`. Note that the vector type provided by this package (`Data.Vector.Unboxing.Vector`) is *different* from `Data.Vector.Unboxed.Vector`.+If you need to convert `Unboxing.Vector` to `Unboxed.Vector`, or vice versa, use `Unboxing.toUnboxedVector` or `Unboxing.fromUnboxedVector`. -The module defining the type `Foo` does not need to export its constructor to enable use of `Vector Foo`.+The module defining the type `Foo` does not need to export its constructor to enable use of `Unboxing.Vector Foo`.+In that case, the users of the abstract data type cannot convert between `Unboxing.Vector Int` and `Unboxing.Vector Foo` --- the abstraction is kept! ## For non-newtypes -Suppose you define a datatype isomorphic to a tuple of primitive types, like:+Suppose you define a data type isomorphic to a tuple, like: ```haskell data ComplexDouble = MkComplexDouble {-# UNPACK #-} !Double {-# UNPACK #-} !Double ``` -In this example, `ComplexDouble` is isomorphic to `(Double, Double)`, but has a different representation. Thus, you cannot derive `Data.Vector.Unboxing.Unboxable` from `(Double, Double)`.+In this example, `ComplexDouble` is isomorphic to `(Double, Double)`, but has a different representation. Thus, you cannot derive `Unboxing.Unboxable` from `(Double, Double)`. For such cases, unboxing-vector provides a feature to derive `Unboxable` using `Generic`.+Use `Unboxing.Generics` newtype wrapper to derive it. ```haskell {-# LANGUAGE DeriveGeneric, DerivingVia, UndecidableInstances #-} -data ComplexDouble = ..+data ComplexDouble = MkComplexDouble {-# UNPACK #-} !Double {-# UNPACK #-} !Double deriving Generic deriving Data.Vector.Unboxing.Unboxable via Data.Vector.Unboxing.Generics ComplexDouble ``` +Unboxing via `fromEnum`/`toEnum` is also available.+Use `Unboxing.Enum` or `Unboxing.EnumRep` to derive it.++```haskell+{-# LANGUAGE DerivingVia, UndecidableInstances #-}++data Direction = North | South | East | West+ deriving Enum+ deriving Unboxing.Unboxable via Unboxing.EnumRep Int8 Direction+```+ ## Conversion ### Conversion from/to Unboxed vector@@ -78,3 +101,15 @@ and :: Unboxing.Vector Bool -> Bool and vec = getAll $ ofold (Unboxing.coerceVector vec :: Unboxing.Vector All) -- fails because the data constructor is not in scope ```++## Supported GHC versions++The library itself is tested with GHC 8.0.2 or later.++To use `GeneralizedNewtypeDeriving` on `Unboxable` class, you need GHC 8.2 or later,+because GND for associated type families became available on that version.++`DerivingVia` is avaliable since GHC 8.6.1.+This means that, defining an `Unboxable` instance for user-defined `data` types (like `ComplexDouble` or `Direction` in this document) requires a latest GHC.++If you want a way to make your `data` types on older GHCs, please [open an issue on GitHub](https://github.com/minoki/unboxing-vector/issues).
src/Data/Vector/Unboxing.hs view
@@ -3,6 +3,8 @@ (Vector ,Unboxable(Rep) ,Generics(..)+ ,Enum(..)+ ,EnumRep(..) -- * Accessors -- ** Length information ,length,null@@ -83,7 +85,8 @@ ,freeze,thaw,copy,unsafeFreeze,unsafeThaw,unsafeCopy ) where -import Prelude (Monad,Int,Bool,Maybe,Traversable,Eq,Num,Enum,Ord,Ordering)+import Prelude (Monad,Int,Bool,Maybe,Traversable,Eq,Num,Ord,Ordering)+import qualified Prelude import qualified Data.Vector.Generic as G import Data.Vector.Generic (convert) import Data.Vector.Unboxing.Internal@@ -266,11 +269,11 @@ enumFromStepN = G.enumFromStepN {-# INLINE enumFromStepN #-} -enumFromTo :: (Enum a, Unboxable a) => a -> a -> Vector a+enumFromTo :: (Prelude.Enum a, Unboxable a) => a -> a -> Vector a enumFromTo = G.enumFromTo {-# INLINE enumFromTo #-} -enumFromThenTo :: (Enum a, Unboxable a) => a -> a -> a -> Vector a+enumFromThenTo :: (Prelude.Enum a, Unboxable a) => a -> a -> a -> Vector a enumFromThenTo = G.enumFromThenTo {-# INLINE enumFromThenTo #-}
src/Data/Vector/Unboxing/Internal.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-}@@ -17,6 +16,8 @@ ,Vector(UnboxingVector) ,MVector(UnboxingMVector) ,Generics(..)+ ,Enum(..)+ ,EnumRep(..) ,coerceVector ,liftCoercion ,vectorCoercion@@ -27,6 +28,8 @@ ,fromUnboxedMVector ,coercionWithUnboxedMVector ) where+import Prelude hiding (Enum)+import qualified Prelude import qualified Data.Vector.Generic as G import qualified Data.Vector.Generic.Mutable as GM import qualified Data.Vector.Unboxed as U@@ -135,9 +138,9 @@ coercionWithUnboxedMVector = Coercion {-# INLINE coercionWithUnboxedMVector #-} -------+-- -- Generics+-- -- | A newtype wrapper to be used with @DerivingVia@. --@@ -148,13 +151,13 @@ -- > deriving Unboxable via Generics Bar newtype Generics a = Generics a -instance (GHC.Generics.Generic a, Unboxable (Rep' (GHC.Generics.Rep a)), Unboxable' (GHC.Generics.Rep a)) => Unboxable (Generics a) where- type Rep (Generics a) = Rep (Rep' (GHC.Generics.Rep a))+instance (GHC.Generics.Generic a, U.Unbox (Rep' (GHC.Generics.Rep a)), Unboxable' (GHC.Generics.Rep a)) => Unboxable (Generics a) where+ type Rep (Generics a) = Rep' (GHC.Generics.Rep a) type CoercibleRep (Generics a) = a type IsTrivial (Generics a) = 'False- unboxingFrom (Generics x) = unboxingFrom (from' (GHC.Generics.from x))+ unboxingFrom (Generics x) = from' (GHC.Generics.from x) {-# INLINE unboxingFrom #-}- unboxingTo y = Generics (GHC.Generics.to (to' (unboxingTo y)))+ unboxingTo y = Generics (GHC.Generics.to (to' y)) {-# INLINE unboxingTo #-} class Unboxable' f where@@ -190,9 +193,48 @@ from' = undefined to' = undefined ------+--+-- Enum+-- +-- | A newtype wrapper to be used with @DerivingVia@.+-- The value will be stored as 'Int', via `fromEnum`/`toEnum`.+--+-- Usage:+--+-- > data Direction = North | South | East | West+-- > deriving Enum+-- > deriving Data.Vector.Unboxing.Unboxable via Data.Vector.Unboxing.Enum Bar+newtype Enum a = Enum a+instance (Prelude.Enum a) => Unboxable (Enum a) where+ type Rep (Enum a) = Int+ type CoercibleRep (Enum a) = a+ type IsTrivial (Enum a) = 'False+ unboxingFrom (Enum x) = fromEnum x+ {-# INLINE unboxingFrom #-}+ unboxingTo y = Enum (toEnum y)+ {-# INLINE unboxingTo #-}++-- | A newtype wrapper to be used with @DerivingVia@.+--+-- Usage:+--+-- > data Direction = North | South | East | West+-- > deriving Enum+-- > deriving Data.Vector.Unboxing.Unboxable via Data.Vector.Unboxing.EnumRep Int8 Bar+newtype EnumRep rep a = EnumRep a+instance (Prelude.Enum a, Integral rep, U.Unbox rep) => Unboxable (EnumRep rep a) where+ type Rep (EnumRep rep a) = rep+ type CoercibleRep (EnumRep rep a) = a+ type IsTrivial (EnumRep rep a) = 'False+ unboxingFrom (EnumRep x) = fromIntegral (fromEnum x)+ {-# INLINE unboxingFrom #-}+ unboxingTo y = EnumRep (toEnum (fromIntegral y))+ {-# INLINE unboxingTo #-}++-- -- Instances+-- instance (Unboxable a) => IsList (Vector a) where type Item (Vector a) = a@@ -223,15 +265,15 @@ {-# INLINE readPrec #-} {-# INLINE readListPrec #-} -instance (Unboxable a) => Semigroup (Vector a) where+instance (Unboxable a) => Data.Semigroup.Semigroup (Vector a) where (<>) = (G.++) sconcat = G.concatNE {-# INLINE (<>) #-} {-# INLINE sconcat #-} -instance (Unboxable a) => Monoid (Vector a) where+instance (Unboxable a) => Data.Monoid.Monoid (Vector a) where mempty = G.empty- mappend = (<>)+ mappend = (Data.Semigroup.<>) mconcat = G.concat {-# INLINE mempty #-} {-# INLINE mappend #-}@@ -283,9 +325,9 @@ {-# INLINE basicUnsafeCopy #-} {-# INLINE elemseq #-} -------+-- -- Classes from mono-traversable+-- type instance Data.MonoTraversable.Element (Vector a) = a @@ -432,9 +474,9 @@ {-# INLINE indexEx #-} {-# INLINE unsafeIndex #-} -------+-- -- Unboxable instances+-- instance Unboxable Bool where type Rep Bool = Bool instance Unboxable Char where type Rep Char = Char@@ -506,16 +548,111 @@ {-# INLINE unboxingFrom #-} {-# INLINE unboxingTo #-} -deriving instance Unboxable a => Unboxable (Data.Functor.Identity.Identity a)-deriving instance Unboxable a => Unboxable (Data.Functor.Const.Const a b)-deriving instance Unboxable a => Unboxable (Data.Semigroup.Min a)-deriving instance Unboxable a => Unboxable (Data.Semigroup.Max a)-deriving instance Unboxable a => Unboxable (Data.Semigroup.First a)-deriving instance Unboxable a => Unboxable (Data.Semigroup.Last a)-deriving instance Unboxable a => Unboxable (Data.Semigroup.WrappedMonoid a)-deriving instance Unboxable a => Unboxable (Data.Monoid.Dual a)-deriving instance Unboxable Data.Monoid.All-deriving instance Unboxable Data.Monoid.Any-deriving instance Unboxable a => Unboxable (Data.Monoid.Sum a)-deriving instance Unboxable a => Unboxable (Data.Monoid.Product a)-deriving instance Unboxable a => Unboxable (Data.Ord.Down a)+{-+GND for type classes with associated type families is not available until GHC 8.2.+With GHC 8.2 or later, one can derive these instances like:++> deriving instance Unboxable a => Unboxable (Data.Functor.Identity.Identity a)+> deriving instance Unboxable a => Unboxable (Data.Functor.Const.Const a b)+> deriving instance Unboxable a => Unboxable (Data.Semigroup.Min a)+> deriving instance Unboxable a => Unboxable (Data.Semigroup.Max a)+> deriving instance Unboxable a => Unboxable (Data.Semigroup.First a)+> deriving instance Unboxable a => Unboxable (Data.Semigroup.Last a)+> deriving instance Unboxable a => Unboxable (Data.Semigroup.WrappedMonoid a)+> deriving instance Unboxable a => Unboxable (Data.Monoid.Dual a)+> deriving instance Unboxable Data.Monoid.All+> deriving instance Unboxable Data.Monoid.Any+> deriving instance Unboxable a => Unboxable (Data.Monoid.Sum a)+> deriving instance Unboxable a => Unboxable (Data.Monoid.Product a)+> deriving instance Unboxable a => Unboxable (Data.Ord.Down a)+-}++instance Unboxable a => Unboxable (Data.Functor.Identity.Identity a) where+ type Rep (Data.Functor.Identity.Identity a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable a => Unboxable (Data.Functor.Const.Const a b) where+ type Rep (Data.Functor.Const.Const a b) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable a => Unboxable (Data.Semigroup.Min a) where+ type Rep (Data.Semigroup.Min a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable a => Unboxable (Data.Semigroup.Max a) where+ type Rep (Data.Semigroup.Max a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable a => Unboxable (Data.Semigroup.First a) where+ type Rep (Data.Semigroup.First a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable a => Unboxable (Data.Semigroup.Last a) where+ type Rep (Data.Semigroup.Last a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable a => Unboxable (Data.Semigroup.WrappedMonoid a) where+ type Rep (Data.Semigroup.WrappedMonoid a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable a => Unboxable (Data.Monoid.Dual a) where+ type Rep (Data.Monoid.Dual a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable Data.Monoid.All where+ type Rep Data.Monoid.All = Bool++instance Unboxable Data.Monoid.Any where+ type Rep Data.Monoid.Any = Bool++instance Unboxable a => Unboxable (Data.Monoid.Sum a) where+ type Rep (Data.Monoid.Sum a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable a => Unboxable (Data.Monoid.Product a) where+ type Rep (Data.Monoid.Product a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable a => Unboxable (Data.Ord.Down a) where+ type Rep (Data.Ord.Down a) = Rep a+ unboxingFrom = coerce (unboxingFrom @a)+ unboxingTo = coerce (unboxingTo @a)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}++instance Unboxable Ordering where+ type Rep Ordering = Int8+ unboxingFrom x = fromIntegral (fromEnum x)+ unboxingTo y = toEnum (fromIntegral y)+ {-# INLINE unboxingFrom #-}+ {-# INLINE unboxingTo #-}
src/Data/Vector/Unboxing/Mutable.hs view
@@ -4,6 +4,8 @@ ,STVector ,Unboxable(Rep) ,Generics(..)+ ,Enum(..)+ ,EnumRep(..) -- * Accessors -- ** Length information ,length,null
+ test-deriving-via/Enum.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE UndecidableInstances #-}+module Enum where+import qualified Data.Vector.Unboxing as V+import Data.Int++data OrderingEq = LT | LE | EQ | GE | GT+ deriving (Eq, Ord, Enum, Bounded, Read, Show)+ deriving V.Unboxable via V.Enum OrderingEq++data Direction = North | South | East | West+ deriving (Eq, Ord, Enum, Bounded, Read, Show)+ deriving V.Unboxable via V.EnumRep Int8 Direction
+ test-deriving-via/Foo.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Foo+ (Foo -- the constructor is not exported!+ ,mkFoo+ ) where+import Data.Vector.Unboxing (Unboxable(..))+import qualified Data.Vector.Unboxed as U+import qualified Data.Vector.Unboxed.Mutable as UM+import qualified Data.Vector.Generic as G+import qualified Data.Vector.Generic.Mutable as GM+import Control.DeepSeq (NFData)+import GHC.Generics+import Data.Coerce++newtype Foo = Foo Int deriving (Eq,Show,Generic)++instance NFData Foo++mkFoo :: Foo+mkFoo = Foo 42++-- Comparison of Data.Vector.Unboxing and Data.Vector.Unboxed:++-- The number of lines needed to enable 'Data.Vector.Unboxing.Vector Foo' is ...++instance Unboxable Foo where+ type Rep Foo = Int -- needs TypeFamilies here++-- ... only 2 lines!+-- Also, you can use GeneralizedNewtypeDeriving + UndecidableInstances if you want to write less.++-- On the other hand, the number of lines needed to enable 'Data.Vector.Unboxed.Vector Foo' is ...++newtype instance UM.MVector s Foo = MV_Foo (UM.MVector s Int)+newtype instance U.Vector Foo = V_Foo (U.Vector Int)++instance GM.MVector UM.MVector Foo where -- needs MultiParamTypeClasses here+ basicLength (MV_Foo mv) = GM.basicLength mv+ basicUnsafeSlice i l (MV_Foo mv) = MV_Foo (GM.basicUnsafeSlice i l mv)+ basicOverlaps (MV_Foo mv) (MV_Foo mv') = GM.basicOverlaps mv mv'+ basicUnsafeNew l = MV_Foo <$> GM.basicUnsafeNew l+ basicInitialize (MV_Foo mv) = GM.basicInitialize mv+ basicUnsafeReplicate i x = MV_Foo <$> GM.basicUnsafeReplicate i (coerce x)+ basicUnsafeRead (MV_Foo mv) i = coerce <$> GM.basicUnsafeRead mv i+ basicUnsafeWrite (MV_Foo mv) i x = GM.basicUnsafeWrite mv i (coerce x)+ basicClear (MV_Foo mv) = GM.basicClear mv+ basicSet (MV_Foo mv) x = GM.basicSet mv (coerce x)+ basicUnsafeCopy (MV_Foo mv) (MV_Foo mv') = GM.basicUnsafeCopy mv mv'+ basicUnsafeMove (MV_Foo mv) (MV_Foo mv') = GM.basicUnsafeMove mv mv'+ basicUnsafeGrow (MV_Foo mv) n = MV_Foo <$> GM.basicUnsafeGrow mv n++instance G.Vector U.Vector Foo where -- needs MultiParamTypeClasses here+ basicUnsafeFreeze (MV_Foo mv) = V_Foo <$> G.basicUnsafeFreeze mv+ basicUnsafeThaw (V_Foo v) = MV_Foo <$> G.basicUnsafeThaw v+ basicLength (V_Foo v) = G.basicLength v+ basicUnsafeSlice i l (V_Foo v) = V_Foo (G.basicUnsafeSlice i l v)+ basicUnsafeIndexM (V_Foo v) i = coerce <$> G.basicUnsafeIndexM v i+ basicUnsafeCopy (MV_Foo mv) (V_Foo v) = G.basicUnsafeCopy mv v+ elemseq (V_Foo v) x y = G.elemseq v (coerce x) y++instance U.Unbox Foo++-- ... enormous!+-- Unfortunately, you cannot use GeneralizedNewtypeDeriving to MVector/Vector classes.
+ test-deriving-via/Generic.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE UndecidableInstances #-}+module Generic where+import Test.HUnit+import qualified Data.Vector.Unboxing as V+import GHC.Generics+import Foo (Foo,mkFoo)+import Enum (Direction(North))++-- Deriving using Generic+data ComplexDouble = ComplexDouble { realPartD :: {-# UNPACK #-} !Double+ , imagPartD :: {-# UNPACK #-} !Double+ }+ deriving (Eq,Show,Generic)+ deriving V.Unboxable via V.Generics ComplexDouble++testComplexDouble = TestCase $ do+ let v :: V.Vector ComplexDouble+ v = V.singleton (ComplexDouble 1.0 2.0)+ x :: V.Vector Double+ x = V.map realPartD v+ assertEqual "construction" (ComplexDouble 1.0 2.0) (V.head v)+ assertEqual "map" (V.singleton 1.0) x++data Bar = Bar {-# UNPACK #-} !Foo {-# UNPACK #-} !Double !Direction+ deriving (Eq,Show,Generic)+ deriving V.Unboxable via V.Generics Bar++testBar = TestCase $ do+ let v :: V.Vector Bar+ v = V.singleton (Bar mkFoo 3.14 North)+ assertEqual "construction" (Bar mkFoo 3.14 North) (V.head v)+ assertEqual "map" (V.singleton mkFoo) (V.map (\(Bar foo _ _) -> foo) v)
+ test-deriving-via/Spec.hs view
@@ -0,0 +1,11 @@+import Prelude+import Test.HUnit+---+import Generic+import Enum++tests = TestList [TestLabel "Test with generic 1" testComplexDouble+ ,TestLabel "Test with generic 2" testBar+ ]++main = runTestTT tests
+ test-deriving-via/TestTypeErrors.hs view
@@ -0,0 +1,19 @@+-- {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fdefer-type-errors -Wno-deferred-type-errors #-}+module TestTypeErrors where+import Test.HUnit+import Test.ShouldNotTypecheck+import qualified Data.Vector.Unboxing as V+import GHC.Generics++data Animal = Dog | Cat+ deriving (Eq,Show,Generic)+ deriving V.Unboxable via V.Generics Animal++testTypeErrors :: Test+testTypeErrors = TestList [TestLabel "Test generic deriving for a sum type" $ TestCase $ shouldNotTypecheck (V.singleton Dog)+ ]+
+ test-gnd/Foo.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE UndecidableInstances #-}+module Foo+ (Foo -- the constructor is not exported!+ ,mkFoo+ ) where+import Data.Vector.Unboxing (Unboxable(..))+import Control.DeepSeq (NFData)+import GHC.Generics++newtype Foo = Foo Int deriving (Eq,Show,Generic,Unboxable)++instance NFData Foo++mkFoo :: Foo+mkFoo = Foo 42
+ test-gnd/Spec.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE UndecidableInstances #-}+import Prelude+import Test.HUnit+import qualified Data.Vector.Unboxing as V+import Data.Monoid (Sum(..))+import Data.MonoTraversable (ofold)+---+import Foo (Foo,mkFoo)++newtype IntMod17 = IntMod17 Int+ deriving (Eq,Show)+ deriving newtype V.Unboxable -- with DerivingStrategies++instance Num IntMod17 where+ IntMod17 x + IntMod17 y = IntMod17 ((x + y) `rem` 17)+ IntMod17 x - IntMod17 y = IntMod17 ((x - y) `mod` 17)+ IntMod17 x * IntMod17 y = IntMod17 ((x * y) `rem` 17)+ negate (IntMod17 x) = IntMod17 (negate x `mod` 17)+ fromInteger x = IntMod17 (fromIntegral (x `mod` 17))+ abs = undefined; signum = undefined++testIntMod17 = TestCase $ do+ let v = V.fromList [-3,-2,-1,0,1,2,3,4,5] :: V.Vector IntMod17+ assertEqual "construction" (V.fromList [14,15,16,0,1,2,3,4,5]) v+ assertEqual "sum" 9 (V.sum v) -- not 60+ assertEqual "coercion" (V.fromList [14,15,16,0,1,2,3,4,5] :: V.Vector Int) (V.coerceVector v) -- this is possible because the constructor of IntMod17 is visible here+ let vSum = V.coerceVector v :: V.Vector (Sum IntMod17)+ assertEqual "coercion and sum" (Sum 9) (ofold vSum)++newtype Baz = Baz Foo+ deriving (Eq,Show,V.Unboxable)++testBaz = TestCase $ do+ let foo :: V.Vector Foo+ foo = V.singleton mkFoo+ baz :: V.Vector Baz+ baz = V.singleton (Baz mkFoo)+ assertEqual "construction" (Baz mkFoo) (V.head baz)+ assertEqual "map 1" baz (V.map Baz foo)+ assertEqual "map 2" foo (V.map (\(Baz x) -> x) baz)+ assertEqual "coercion" baz (V.coerceVector foo)++-- We can make an unboxed vector of Foo, even though we don't have 'Coercible Int Foo' in scope.+testAbstractType = TestCase $ do+ let v = V.singleton mkFoo :: V.Vector Foo+ assertEqual "Foo" mkFoo (V.head v)+ assertEqual "coercion" mkFoo (getSum $ V.head (V.coerceVector v :: V.Vector (Sum Foo)))++tests = TestList [TestLabel "Basic features" testIntMod17+ ,TestLabel "Test with abstract type" testAbstractType+ ,TestLabel "Test with GND" testBaz+ ]++main = runTestTT tests
− test/Generic.hs
@@ -1,33 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingVia #-}-{-# LANGUAGE UndecidableInstances #-}-module Generic where-import Test.HUnit-import qualified Data.Vector.Unboxing as V-import GHC.Generics-import Foo (Foo,mkFoo)---- Deriving using Generic-data ComplexDouble = ComplexDouble { realPartD :: {-# UNPACK #-} !Double- , imagPartD :: {-# UNPACK #-} !Double- }- deriving (Eq,Show,Generic)- deriving V.Unboxable via V.Generics ComplexDouble--testComplexDouble = TestCase $ do- let v :: V.Vector ComplexDouble- v = V.singleton (ComplexDouble 1.0 2.0)- x :: V.Vector Double- x = V.map realPartD v- assertEqual "construction" (ComplexDouble 1.0 2.0) (V.head v)- assertEqual "map" (V.singleton 1.0) x--data Bar = Bar {-# UNPACK #-} !Foo {-# UNPACK #-} !Double- deriving (Eq,Show,Generic)- deriving V.Unboxable via V.Generics Bar--testBar = TestCase $ do- let v :: V.Vector Bar- v = V.singleton (Bar mkFoo 3.14)- assertEqual "construction" (Bar mkFoo 3.14) (V.head v)- assertEqual "map" (V.singleton mkFoo) (V.map (\(Bar foo _) -> foo) v)
+ test/Loop.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DerivingStrategies #-}+-- {-# LANGUAGE DerivingVia #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeFamilies #-}+module Loop where+import qualified Data.Vector.Unboxing as V+import GHC.Generics++-- Loop!+{-+newtype Bad = Bad Bad+ deriving newtype V.Unboxable+-}++{-+data Bad2 = Bad2 Bad2+ deriving Generic+ deriving V.Unboxable via V.Generics Bad2+-}++{-+class SomeClass a where+ type Foo a+-- foo :: a -> Foo a++newtype Bad = Bad Bad+ deriving newtype SomeClass++newtype BadT = BadT (Int, BadT)+ deriving newtype V.Unboxable+-}
test/Spec.hs view
@@ -11,7 +11,6 @@ --- import TestTypeErrors import Foo (Foo,mkFoo)-import Generic testInt = TestCase $ do let v = V.fromList [2,-5,42] :: V.Vector Int@@ -21,7 +20,6 @@ newtype IntMod17 = IntMod17 Int deriving (Eq,Show)--- deriving newtype VF.Unboxable instance V.Unboxable IntMod17 where type Rep IntMod17 = Int@@ -42,19 +40,6 @@ let vSum = V.coerceVector v :: V.Vector (Sum IntMod17) assertEqual "coercion and sum" (Sum 9) (ofold vSum) -newtype Baz = Baz Foo- deriving (Eq,Show,V.Unboxable)--testBaz = TestCase $ do- let foo :: V.Vector Foo- foo = V.singleton mkFoo- baz :: V.Vector Baz- baz = V.singleton (Baz mkFoo)- assertEqual "construction" (Baz mkFoo) (V.head baz)- assertEqual "map 1" baz (V.map Baz foo)- assertEqual "map 2" foo (V.map (\(Baz x) -> x) baz)- assertEqual "coercion" baz (V.coerceVector foo)- -- We can make an unboxed vector of Foo, even though we don't have 'Coercible Int Foo' in scope. testAbstractType = TestCase $ do let v = V.singleton mkFoo :: V.Vector Foo@@ -65,10 +50,6 @@ ,TestLabel "Conversion with Data.Vector.Unboxed" testInt ,TestLabel "Test with abstract type" testAbstractType ,TestLabel "Check for type errors" testTypeErrors- ,TestLabel "Test with generic 1" testComplexDouble- ,TestLabel "Test with generic 2" testBar- ,TestLabel "Test with GND" testBaz ] main = runTestTT tests-
test/TestTypeErrors.hs view
@@ -1,7 +1,4 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingVia #-}-{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fdefer-type-errors -Wno-deferred-type-errors #-} module TestTypeErrors where import Test.HUnit@@ -9,7 +6,6 @@ import Data.Coerce import qualified Data.Vector.Unboxing as V import Foo (Foo)-import GHC.Generics -- Since the module Foo does not export Foo's constructor, -- it should be impossible to create a value of Foo in this module.@@ -30,14 +26,9 @@ intToFoo3 :: (V.Unboxable a, a ~ Foo) => Int -> a intToFoo3 x = coerce x -data Animal = Dog | Cat- deriving (Eq,Show,Generic)- deriving V.Unboxable via V.Generics Animal- testTypeErrors :: Test testTypeErrors = TestList [TestLabel "Basic test for coerce" $ TestCase $ shouldNotTypecheck (intToFoo1 0xDEAD) ,TestLabel "Test for coerceVector" $ TestCase $ shouldNotTypecheck (intToFoo2 0xDEAD) ,TestLabel "Test for Unboxable" $ TestCase $ shouldNotTypecheck (intToFoo3 0xDEAD)- ,TestLabel "Test generic deriving for a sum type" $ TestCase $ shouldNotTypecheck (V.singleton Dog) ]
unboxing-vector.cabal view
@@ -4,11 +4,11 @@ -- -- see: https://github.com/sol/hpack ----- hash: 6f43e3e12c9aa312cf1e18e1bc60132a5cb9878c463bd2b9863a20ea6c7125cf+-- hash: 8fec698d5a4391032074d2a71e767bc6f667b179e962ed429de29ca13c44b918 name: unboxing-vector-version: 0.1.0.0-synopsis: Newtype-friendly Unboxed Vectors+version: 0.1.1.0+synopsis: A newtype-friendly variant of unboxed vectors description: Please see the README on GitHub at <https://github.com/minoki/unboxing-vector#readme> category: Data, Data Structures homepage: https://github.com/minoki/unboxing-vector#readme@@ -49,7 +49,7 @@ main-is: Spec.hs other-modules: Foo- Generic+ Loop TestTypeErrors Paths_unboxing_vector hs-source-dirs:@@ -64,6 +64,56 @@ , should-not-typecheck , unboxing-vector , vector+ default-language: Haskell2010++test-suite unboxing-vector-test-deriving-via+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Enum+ Foo+ Generic+ TestTypeErrors+ Paths_unboxing_vector+ hs-source-dirs:+ test-deriving-via+ ghc-options: -Wall -Wno-missing-signatures+ build-depends:+ HUnit+ , base >=4.9 && <5+ , deepseq+ , mono-traversable+ , primitive+ , should-not-typecheck+ , unboxing-vector+ , vector+ if impl(ghc >= 8.6.1)+ buildable: True+ else+ buildable: False+ default-language: Haskell2010++test-suite unboxing-vector-test-gnd+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Foo+ Paths_unboxing_vector+ hs-source-dirs:+ test-gnd+ ghc-options: -Wall -Wno-missing-signatures+ build-depends:+ HUnit+ , base >=4.9 && <5+ , deepseq+ , mono-traversable+ , primitive+ , unboxing-vector+ , vector+ if impl(ghc >= 8.2.1)+ buildable: True+ else+ buildable: False default-language: Haskell2010 benchmark unboxing-vector-benchmark