vinyl 0.5 → 0.5.1
raw patch · 6 files changed
+104/−30 lines, 6 filesdep ~vinylnew-uploaderPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: vinyl
API changes (from Hackage documentation)
+ Data.Vinyl.Derived: (=:) :: KnownSymbol s => proxy '(s, a) -> a -> FieldRec '['(s, a)]
+ Data.Vinyl.Derived: SField :: SField
+ Data.Vinyl.Derived: data SField (field :: k)
+ Data.Vinyl.Derived: fieldMap :: (a -> b) -> ElField '(s, a) -> ElField '(s, b)
+ Data.Vinyl.Derived: getField :: ElField '(s, t) -> t
+ Data.Vinyl.Derived: instance (KnownSymbol s, Storable t) => Storable (ElField '(s, t))
+ Data.Vinyl.Derived: instance Eq (SField a)
+ Data.Vinyl.Derived: instance Eq t => Eq (ElField '(s, t))
+ Data.Vinyl.Derived: instance KnownSymbol s => Show (SField '(s, t))
+ Data.Vinyl.Derived: instance Ord (SField a)
+ Data.Vinyl.Derived: instance Ord t => Ord (ElField '(s, t))
+ Data.Vinyl.Derived: instance Show t => Show (ElField '(s, t))
+ Data.Vinyl.Derived: rfield :: Functor f => (a -> f b) -> ElField '(s, a) -> f (ElField '(s, b))
- Data.Vinyl.Derived: Field :: t -> ElField '(s, t)
+ Data.Vinyl.Derived: Field :: !t -> ElField '(s, t)
Files
- CHANGELOG.md +27/−0
- Data/Vinyl/Core.hs +2/−1
- Data/Vinyl/Derived.hs +47/−2
- benchmarks/StorableBench.hs +9/−9
- tests/Intro.lhs +17/−17
- vinyl.cabal +2/−1
+ CHANGELOG.md view
@@ -0,0 +1,27 @@+0.5.1+---------++Added utilities for working with the `FieldRec` type.++Vinyl 0.5+=========++Vinyl 0.5 combines the generality of Vinyl 0.4 with the ease-of-use of previous+versions by eschewing the defunctionalized type families and just using plain+type constructors; Vinyl 0.4-style records can be recovered in most cases in a+modular manner without baking it into the fabric of Vinyl itself.++Also new in 0.5 is a unified lens-based approach to subtyping, coercion and+projection.++Vinyl 0.4+=========++Vinyl 0.4 is a big departure from previous versions, in that it introduces a+universe encoding as a means to generalize the space of keys from strings to+any arbitrary space. This means that you can have closed universes for your+records.++For details on how to use the new Vinyl, please see `tests/Intro.lhs` or view+Jon's talk at BayHac 2014, [Programming in+Vinyl](http://www.jonmsterling.com/posts/2014-05-19-programming-in-vinyl-bayhac.html).
Data/Vinyl/Core.hs view
@@ -119,6 +119,7 @@ -> h (Rec g rs) rtraverse _ RNil = pure RNil rtraverse f (x :& xs) = (:&) <$> f x <*> rtraverse f xs+{-# INLINABLE rtraverse #-} -- | A record with uniform fields may be turned into a list. recordToList@@ -154,7 +155,7 @@ instance RecAll f rs Show => Show (Rec f rs) where show xs = (\str -> "{" <> str <> "}")- . intercalate "; "+ . intercalate ", " . recordToList . rmap (\(Compose (Dict x)) -> Const $ show x) $ reifyConstraint (Proxy :: Proxy Show) xs
Data/Vinyl/Derived.hs view
@@ -1,18 +1,63 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-} module Data.Vinyl.Derived where +import Data.Proxy import Data.Vinyl.Core import Data.Vinyl.Functor-+import Foreign.Ptr (castPtr)+import Foreign.Storable import GHC.TypeLits data ElField (field :: (Symbol, *)) where- Field :: KnownSymbol s => t -> ElField '(s,t)+ Field :: KnownSymbol s => !t -> ElField '(s,t) type FieldRec = Rec ElField type HList = Rec Identity type LazyHList = Rec Thunk++deriving instance Eq t => Eq (ElField '(s,t))+deriving instance Ord t => Ord (ElField '(s,t))++instance Show t => Show (ElField '(s,t)) where+ show (Field x) = (symbolVal (Proxy::Proxy s))++" :-> "++show x++-- | Get the data payload of an 'ElField'.+getField :: ElField '(s,t) -> t+getField (Field x) = x++-- | 'ElField' is isomorphic to a functor something like @Compose+-- ElField ('(,) s)@.+fieldMap :: (a -> b) -> ElField '(s,a) -> ElField '(s,b)+fieldMap f (Field x) = Field (f x)+{-# INLINE fieldMap #-}++-- | Lens for an 'ElField''s data payload.+rfield :: Functor f => (a -> f b) -> ElField '(s,a) -> f (ElField '(s,b))+rfield f (Field x) = fmap Field (f x)+{-# INLINE rfield #-}++-- | Shorthand for a 'FieldRec' with a single field.+(=:) :: KnownSymbol s => proxy '(s,a) -> a -> FieldRec '[ '(s,a) ]+(=:) _ x = Field x :& RNil++-- | A proxy for field types.+data SField (field :: k) = SField++instance Eq (SField a) where _ == _ = True+instance Ord (SField a) where compare _ _ = EQ+instance KnownSymbol s => Show (SField '(s,t)) where+ show _ = "SField "++symbolVal (Proxy::Proxy s)++instance forall s t. (KnownSymbol s, Storable t)+ => Storable (ElField '(s,t)) where+ sizeOf _ = sizeOf (undefined::t)+ alignment _ = alignment (undefined::t)+ peek ptr = Field `fmap` peek (castPtr ptr)+ poke ptr (Field x) = poke (castPtr ptr) x
benchmarks/StorableBench.hs view
@@ -11,10 +11,10 @@ import Control.Lens import Control.Monad (when) import qualified Data.Foldable as F+import Data.Proxy import qualified Data.Vector.Storable as V import qualified Data.Vector.Storable.Mutable as VM import Data.Vinyl-import Data.Vinyl.Universe.Field import Foreign.Ptr (castPtr) import Foreign.Storable (Storable(..)) import Linear (V2, V3, _y)@@ -28,23 +28,23 @@ randVecStd :: (Storable a, Variate a) => Int -> IO (V.Vector a) randVecStd = withSystemRandom . randVec -vNorm :: SField ("normal" ::: V3 a)-vNorm = SField+vNorm :: Proxy '("normal", V3 a)+vNorm = Proxy -type MyFields a = [ "pos" ::: V3 a, "tex" ::: V2 a, "normal" ::: V3 a ]-type MyVertex a = PlainRec ElField (MyFields a)+type MyFields a = [ '("pos", V3 a), '("tex", V2 a), '("normal", V3 a) ]+type MyVertex a = FieldRec (MyFields a) doubleNviL :: V.Vector (MyVertex Float) -> V.Vector (MyVertex Float)-doubleNviL = V.map (rLens vNorm . _y *~ (2::Float))+doubleNviL = V.map (rlens vNorm . rfield . _y *~ (2::Float)) vinylNSumL :: (Num a, Storable a) => V.Vector (MyVertex a) -> a-vinylNSumL = V.sum . V.map (F.sum . view (rLens vNorm))+vinylNSumL = V.sum . V.map (F.sum . view (rlens vNorm . rfield)) doubleNvi :: V.Vector (MyVertex Float) -> V.Vector (MyVertex Float)-doubleNvi = V.map (rMod vNorm (_y *~ (2::Float)))+doubleNvi = V.map (rlens vNorm . rfield . _y *~ (2::Float)) vinylNSum :: (Num a, Storable a) => V.Vector (MyVertex a) -> a-vinylNSum = V.sum . V.map (F.sum . rGet vNorm)+vinylNSum = V.sum . V.map (F.sum . view rfield . rget vNorm) main :: IO () main = do vals <- randVecStd $ n * 8 :: IO (V.Vector Float)
tests/Intro.lhs view
@@ -47,31 +47,31 @@ > instance Show (Attr Sleeping) where show (Attr x) = "sleeping: " ++ show x > instance Show (Attr Master) where show (Attr x) = "master: " ++ show x -> (=:) :: sing f -> ElF f -> Attr f-> _ =: x = Attr x+> (=::) :: sing f -> ElF f -> Attr f+> _ =:: x = Attr x > genSingletons [ ''Fields ] Now, let’s try to make an entity that represents a man: -> jon = (SName =: "jon")-> :& (SAge =: 23)-> :& (SSleeping =: False)+> jon = (SName =:: "jon")+> :& (SAge =:: 23)+> :& (SSleeping =:: False) > :& RNil Automatically, we can show the record: > -- | > -- >>> show jon-> -- "{name: \"jon\"; age: 23; sleeping: False}"+> -- "{name: \"jon\", age: 23, sleeping: False}" And its types are all inferred with no problem. Now, make a dog! Dogs are life-forms, but unlike men, they have masters. So, let’s build my dog: -> tucker = (SName =: "tucker")-> :& (SAge =: 9)-> :& (SSleeping =: True)-> :& (SMaster =: jon)+> tucker = (SName =:: "tucker")+> :& (SAge =:: 9)+> :& (SSleeping =:: True)+> :& (SMaster =:: jon) > :& RNil Using Lenses@@ -85,7 +85,7 @@ > wakeUp :: (Sleeping ∈ fields) => Rec Attr fields -> Rec Attr fields-> wakeUp = rput $ SSleeping =: False+> wakeUp = rput $ SSleeping =:: False Now, the type annotation on wakeUp was not necessary; I just wanted to show how intuitive the type is. Basically, it takes as an input any@@ -110,7 +110,7 @@ update on a record: > masterSleeping = rlens SMaster . unAttr . rlens SSleeping-> tucker'' = masterSleeping .~ (SSleeping =: True) $ tucker'+> tucker'' = masterSleeping .~ (SSleeping =:: True) $ tucker' > -- | >>> tucker'' ^. masterSleeping > -- sleeping: True@@ -169,18 +169,18 @@ left-accumulating `Validation` type. > goodPerson :: Rec Attr Person-> goodPerson = (SName =: "Jon")-> :& (SAge =: 20)+> goodPerson = (SName =:: "Jon")+> :& (SAge =:: 20) > :& RNil -> badPerson = (SName =: "J#@#$on")-> :& (SAge =: 20)+> badPerson = (SName =:: "J#@#$on")+> :& (SAge =:: 20) > :& RNil We'll give validation a (rather poor) shot. > validatePerson :: Rec Attr Person -> Maybe (Rec Attr Person)-> validatePerson p = (\n a -> (SName =: n) :& (SAge =: a) :& RNil) <$> vName <*> vAge where+> validatePerson p = (\n a -> (SName =:: n) :& (SAge =:: a) :& RNil) <$> vName <*> vAge where > vName = validateName $ p ^. rlens SName . unAttr > vAge = validateAge $ p ^. rlens SAge . unAttr >
vinyl.cabal view
@@ -1,5 +1,5 @@ name: vinyl-version: 0.5+version: 0.5.1 synopsis: Extensible Records -- description: license: MIT@@ -11,6 +11,7 @@ stability: Experimental build-type: Simple cabal-version: >=1.10+extra-source-files: CHANGELOG.md description: Extensible records for Haskell with lenses using modern GHC features.