row-types-0.3.0.0: Open Records and Variants

Safe HaskellNone
LanguageHaskell98

Data.Row.Records

Contents

Description

This module implements extensible records using closed type famillies.

See Examples.hs for examples.

Lists of (label,type) pairs are kept sorted thereby ensuring that { x = 0, y = 0 } and { y = 0, x = 0 } have the same type.

In this way we can implement standard type classes such as Show, Eq, Ord and Bounded for open records, given that all the elements of the open record satify the constraint.

Synopsis

Types and constraints

data Label (s :: Symbol) Source #

A label

Constructors

Label 
Instances
x y => IsLabel x (Label y) Source # 
Instance details

Defined in Data.Row.Internal

Methods

fromLabel :: Label y #

Eq (Label s) Source # 
Instance details

Defined in Data.Row.Internal

Methods

(==) :: Label s -> Label s -> Bool #

(/=) :: Label s -> Label s -> Bool #

KnownSymbol s => Show (Label s) Source # 
Instance details

Defined in Data.Row.Internal

Methods

showsPrec :: Int -> Label s -> ShowS #

show :: Label s -> String #

showList :: [Label s] -> ShowS #

class KnownSymbol (n :: Symbol) #

This class gives the string associated with a type-level symbol. There are instances of the class for every concrete literal: "hello", etc.

Since: base-4.7.0.0

Minimal complete definition

symbolSing

type family AllUniqueLabels (r :: Row k) :: Constraint where ... Source #

Are all of the labels in this Row unique?

Equations

AllUniqueLabels (R r) = AllUniqueLabelsR r 

type WellBehaved ρ = (Forall ρ Unconstrained1, AllUniqueLabels ρ) Source #

A convenient way to provide common, easy constraints

data Rec (r :: Row *) Source #

A record with row r.

Instances
(KnownSymbol name, (r .! name) a, r ~ Modify name a r) => HasField' name (Rec r) a Source # 
Instance details

Defined in Data.Row.Records

Methods

field' :: Lens (Rec r) (Rec r) a a #

(KnownSymbol name, (r' .! name) b, (r .! name) a, r' ~ Modify name b r, r ~ Modify name a r') => HasField name (Rec r) (Rec r') a b Source #

Every field in a row-types based record has a HasField instance.

Instance details

Defined in Data.Row.Records

Methods

field :: Lens (Rec r) (Rec r') a b #

(Forall r Bounded, AllUniqueLabels r) => Bounded (Rec r) Source # 
Instance details

Defined in Data.Row.Records

Methods

minBound :: Rec r #

maxBound :: Rec r #

Forall r Eq => Eq (Rec r) Source # 
Instance details

Defined in Data.Row.Records

Methods

(==) :: Rec r -> Rec r -> Bool #

(/=) :: Rec r -> Rec r -> Bool #

(Forall r Eq, Forall r Ord) => Ord (Rec r) Source # 
Instance details

Defined in Data.Row.Records

Methods

compare :: Rec r -> Rec r -> Ordering #

(<) :: Rec r -> Rec r -> Bool #

(<=) :: Rec r -> Rec r -> Bool #

(>) :: Rec r -> Rec r -> Bool #

(>=) :: Rec r -> Rec r -> Bool #

max :: Rec r -> Rec r -> Rec r #

min :: Rec r -> Rec r -> Rec r #

Forall r Show => Show (Rec r) Source # 
Instance details

Defined in Data.Row.Records

Methods

showsPrec :: Int -> Rec r -> ShowS #

show :: Rec r -> String #

showList :: [Rec r] -> ShowS #

GenericRec r => Generic (Rec r) Source # 
Instance details

Defined in Data.Row.Records

Associated Types

type Rep (Rec r) :: Type -> Type #

Methods

from :: Rec r -> Rep (Rec r) x #

to :: Rep (Rec r) x -> Rec r #

Forall r NFData => NFData (Rec r) Source # 
Instance details

Defined in Data.Row.Records

Methods

rnf :: Rec r -> () #

type Rep (Rec r) Source # 
Instance details

Defined in Data.Row.Records

type Rep (Rec r)

data Row a Source #

The kind of rows. This type is only used as a datakind. A row is a typelevel entity telling us which symbols are associated with which types.

type Empty = R '[] Source #

Type level version of empty

type (≈) a b = a ~ b infix 4 Source #

A lower fixity operator for type equality

Construction

empty :: Rec Empty Source #

The empty record

type (.==) (l :: Symbol) (a :: k) = Extend l a Empty infix 7 Source #

A type level way to create a singleton Row.

(.==) :: KnownSymbol l => Label l -> a -> Rec (l .== a) infix 7 Source #

The singleton record

pattern (:==) :: forall l a. KnownSymbol l => Label l -> a -> Rec (l .== a) infix 7 Source #

A pattern for the singleton record; can be used to both destruct a record when in a pattern position or construct one in an expression position.

unSingleton :: forall l a. KnownSymbol l => Rec (l .== a) -> (Label l, a) Source #

Turns a singleton record into a pair of the label and value.

default' :: forall c ρ. (Forall ρ c, AllUniqueLabels ρ) => (forall a. c a => a) -> Rec ρ Source #

Initialize a record with a default value at each label.

defaultA :: forall c f ρ. (Applicative f, Forall ρ c, AllUniqueLabels ρ) => (forall a. c a => f a) -> f (Rec ρ) Source #

Initialize a record with a default value at each label; works over an Applicative.

fromLabels :: forall c ρ. (Forall ρ c, AllUniqueLabels ρ) => (forall l a. (KnownSymbol l, c a) => Label l -> a) -> Rec ρ Source #

Initialize a record, where each value is determined by the given function over the label at that value.

fromLabelsA :: forall c f ρ. (Applicative f, Forall ρ c, AllUniqueLabels ρ) => (forall l a. (KnownSymbol l, c a) => Label l -> f a) -> f (Rec ρ) Source #

Initialize a record, where each value is determined by the given function over the label at that value. This function works over an Applicative.

fromLabelsMapA :: forall c f g ρ. (Applicative f, Forall ρ c, AllUniqueLabels ρ) => (forall l a. (KnownSymbol l, c a) => Label l -> f (g a)) -> f (Rec (Map g ρ)) Source #

Initialize a record that is produced by a Map.

Extension

extend :: forall a l r. KnownSymbol l => Label l -> a -> Rec r -> Rec (Extend l a r) Source #

Record extension. The row may already contain the label, in which case the origin value can be obtained after restriction (.-) with the label.

type family Extend (l :: Symbol) (a :: k) (r :: Row k) :: Row k where ... Source #

Type level Row extension

Equations

Extend l a (R x) = R (Inject (l :-> a) x) 

class Lacks (l :: Symbol) (r :: Row *) Source #

Alias for .\. It is a class rather than an alias, so that it can be partially applied.

Instances
r .\ l => Lacks l r Source # 
Instance details

Defined in Data.Row.Internal

type family (r :: Row k) .\ (l :: Symbol) :: Constraint where ... infixl 4 Source #

Does the row lack (i.e. it does not have) the specified label?

Equations

(R r) .\ l = LacksR l r r 

Restriction

type family (r :: Row k) .- (s :: Symbol) :: Row k where ... infixl 6 Source #

Type level Row element removal

Equations

(R r) .- l = R (Remove l r) 

(.-) :: KnownSymbol l => Rec r -> Label l -> Rec (r .- l) infixl 6 Source #

Record restriction. Remove the label l from the record.

restrict :: forall r r'. (Forall r Unconstrained1, Subset r r') => Rec r' -> Rec r Source #

Arbitrary record restriction. Turn a record into a subset of itself.

split :: forall s r. (Forall s Unconstrained1, Subset s r) => Rec r -> (Rec s, Rec (r .\\ s)) Source #

Split a record into two sub-records.

Modification

update :: (KnownSymbol l, (r .! l) a) => Label l -> a -> Rec r -> Rec r Source #

Update the value associated with the label.

focus :: (KnownSymbol l, (r' .! l) b, (r .! l) a, r' ~ Modify l b r, r ~ Modify l a r', Functor f) => Label l -> (a -> f b) -> Rec r -> f (Rec r') Source #

Focus on the value associated with the label.

multifocus :: forall u v r f. (Functor f, Disjoint u r, Disjoint v r) => (Rec u -> f (Rec v)) -> Rec (u .+ r) -> f (Rec (v .+ r)) Source #

Focus on a sub-record

type family Modify (l :: Symbol) (a :: k) (r :: Row k) :: Row k where ... Source #

Type level Row modification

Equations

Modify l a (R ρ) = R (ModifyR l a ρ) 

rename :: (KnownSymbol l, KnownSymbol l') => Label l -> Label l' -> Rec r -> Rec (Rename l l' r) Source #

Rename a label.

type family Rename (l :: Symbol) (l' :: Symbol) (r :: Row k) :: Row k where ... Source #

Type level row renaming

Equations

Rename l l' r = Extend l' (r .! l) (r .- l) 

Query

class (r .! l) a => HasType l a r Source #

Alias for (r .! l) ≈ a. It is a class rather than an alias, so that it can be partially applied.

Instances
(r .! l) a => HasType l (a :: k) (r :: Row k) Source # 
Instance details

Defined in Data.Row.Internal

type family (r :: Row k) .! (t :: Symbol) :: k where ... infixl 5 Source #

Type level label fetching

Equations

(R r) .! l = Get l r 

(.!) :: KnownSymbol l => Rec r -> Label l -> r .! l Source #

Record selection

Combine

Disjoint union

type family (l :: Row k) .+ (r :: Row k) :: Row k where ... infixl 6 Source #

Type level Row append

Equations

(R l) .+ (R r) = R (Merge l r) 

(.+) :: Rec l -> Rec r -> Rec (l .+ r) infixl 6 Source #

Record disjoint union (commutative)

type Disjoint l r = (WellBehaved l, WellBehaved r, Subset l (l .+ r), Subset r (l .+ r), ((l .+ r) .\\ l) r, ((l .+ r) .\\ r) l) Source #

A type synonym for disjointness.

pattern (:+) :: forall l r. Disjoint l r => Rec l -> Rec r -> Rec (l .+ r) infixl 6 Source #

A pattern version of record union, for use in pattern matching.

Overwrite

type family (l :: Row k) .// (r :: Row k) where ... infixl 6 Source #

The overwriting union, where the left row overwrites the types of the right row where the labels overlap.

Equations

(R l) .// (R r) = R (ConstUnionR l r) 

(.//) :: Rec r -> Rec r' -> Rec (r .// r') Source #

Record overwrite.

The operation r .// r' creates a new record such that:

  • Any label that is in both r and r' is in the resulting record with the type and value given by the fields in r,
  • Any label that is only found in r is in the resulting record.
  • Any label that is only found in r' is in the resulting record.

This can be thought of as r "overwriting" r'.

Native Conversion

The toNative and fromNative functions allow one to convert between Recs and regular Haskell data types ("native" types) that have a single constructor and any number of named fields with the same names and types as the Rec. That said, they do not compose to form the identity because toNative allows fields to be dropped: a record with excess fields can still be transformed to a native type, but when the native type is converted to a record, the fields are exactly transformed. Because of this, toNative requires a type application (although fromNative does not). The only requirement is that the native Haskell data type be an instance of Generic.

For example, consider the following simple data type:

>>> data Person = Person { name :: String, age :: Int} deriving (Generic, Show)

Then, we have the following:

>>> toNative @Person $ #name .== "Alice" .+ #age .== 7 .+ #hasDog .== True
Person {name = "Alice", age = 7}
>>> fromNative $ Person "Bob" 9
{ age=9, name="Bob" }

The toNativeExact function is a more restricted version of toNative that does not allow fields to be dropped; in other words, the fields in the record must exactly match the fields in the data type. Because of this, toNativeExact and fromNative compose to form the identity function.

toNative :: forall t ρ. (Generic t, ToNative (Rep t) ρ) => Rec ρ -> t Source #

Convert a record to a native Haskell type.

toNativeExact :: forall t ρ. (Generic t, ToNativeExact (Rep t) ρ) => Rec ρ -> t Source #

Convert a record to an exactly matching native Haskell type.

fromNative :: forall t ρ. (Generic t, FromNative (Rep t) ρ) => t -> Rec ρ Source #

Convert a Haskell record to a row-types Rec.

Dynamic Conversion

Row operations

Map

type family Map (f :: a -> b) (r :: Row a) :: Row b where ... Source #

Map a type level function over a Row.

Equations

Map f (R r) = R (MapR f r) 

map :: forall c f r. Forall r c => (forall a. c a => a -> f a) -> Rec r -> Rec (Map f r) Source #

A function to map over a record given a constraint.

map' :: forall f r. Forall r Unconstrained1 => (forall a. a -> f a) -> Rec r -> Rec (Map f r) Source #

A function to map over a record given no constraint.

mapF :: forall c g (ϕ :: Row (k -> *)) (ρ :: Row k). BiForall ϕ ρ c => (forall f a. c f a => f a -> f (g a)) -> Rec (Ap ϕ ρ) -> Rec (Ap ϕ (Map g ρ)) Source #

A function to map over a Ap record given constraints.

transform :: forall c r (f :: * -> *) (g :: * -> *). Forall r c => (forall a. c a => f a -> g a) -> Rec (Map f r) -> Rec (Map g r) Source #

Lifts a natural transformation over a record. In other words, it acts as a record transformer to convert a record of f a values to a record of g a values. If no constraint is needed, instantiate the first type argument with Unconstrained1 or use transform'.

transform' :: forall r (f :: * -> *) (g :: * -> *). Forall r Unconstrained1 => (forall a. f a -> g a) -> Rec (Map f r) -> Rec (Map g r) Source #

A version of transform for when there is no constraint.

Fold

class Forall (r :: Row k) (c :: k -> Constraint) Source #

Any structure over a row in which every element is similarly constrained can be metamorphized into another structure over the same row.

Minimal complete definition

metamorph, metamorph'

Instances
(KnownSymbol ℓ, c τ, Forall (R ρ) c) => Forall (R ((ℓ :-> τ) ': ρ) :: Row k) (c :: k -> Constraint) Source # 
Instance details

Defined in Data.Row.Internal

Methods

metamorph :: Proxy h -> (f Empty -> g Empty) -> (forall (ℓ0 :: Symbol) (τ0 :: k0) (ρ0 :: [LT k0]). (KnownSymbol ℓ0, c τ0) => Label ℓ0 -> f (R ((ℓ0 :-> τ0) ': ρ0)) -> (h τ0, f (R ρ0))) -> (forall (ℓ1 :: Symbol) (τ1 :: k0) (ρ1 :: [LT k0]). (KnownSymbol ℓ1, c τ1) => Label ℓ1 -> h τ1 -> g (R ρ1) -> g (R ((ℓ1 :-> τ1) ': ρ1))) -> f (R ((ℓ :-> τ) ': ρ)) -> g (R ((ℓ :-> τ) ': ρ)) Source #

metamorph' :: Proxy h -> (f Empty -> g Empty) -> (forall (ℓ0 :: Symbol) (τ0 :: k0) (ρ0 :: [LT k0]). (KnownSymbol ℓ0, c τ0) => Label ℓ0 -> f (R ((ℓ0 :-> τ0) ': ρ0)) -> Either (h τ0) (f (R ρ0))) -> (forall (ℓ1 :: Symbol) (τ1 :: k0) (ρ1 :: [LT k0]). (KnownSymbol ℓ1, c τ1) => Label ℓ1 -> Either (h τ1) (g (R ρ1)) -> g (R ((ℓ1 :-> τ1) ': ρ1))) -> f (R ((ℓ :-> τ) ': ρ)) -> g (R ((ℓ :-> τ) ': ρ)) Source #

Forall (R ([] :: [LT k]) :: Row k) (c :: k -> Constraint) Source # 
Instance details

Defined in Data.Row.Internal

Methods

metamorph :: Proxy h -> (f Empty -> g Empty) -> (forall (ℓ :: Symbol) (τ :: k0) (ρ :: [LT k0]). (KnownSymbol ℓ, c τ) => Label ℓ -> f (R ((ℓ :-> τ) ': ρ)) -> (h τ, f (R ρ))) -> (forall (ℓ :: Symbol) (τ :: k0) (ρ :: [LT k0]). (KnownSymbol ℓ, c τ) => Label ℓ -> h τ -> g (R ρ) -> g (R ((ℓ :-> τ) ': ρ))) -> f (R []) -> g (R []) Source #

metamorph' :: Proxy h -> (f Empty -> g Empty) -> (forall (ℓ :: Symbol) (τ :: k0) (ρ :: [LT k0]). (KnownSymbol ℓ, c τ) => Label ℓ -> f (R ((ℓ :-> τ) ': ρ)) -> Either (h τ) (f (R ρ))) -> (forall (ℓ :: Symbol) (τ :: k0) (ρ :: [LT k0]). (KnownSymbol ℓ, c τ) => Label ℓ -> Either (h τ) (g (R ρ)) -> g (R ((ℓ :-> τ) ': ρ))) -> f (R []) -> g (R []) Source #

erase :: forall c ρ b. Forall ρ c => (forall a. c a => a -> b) -> Rec ρ -> [b] Source #

A standard fold

eraseWithLabels :: forall c ρ s b. (Forall ρ c, IsString s) => (forall a. c a => a -> b) -> Rec ρ -> [(s, b)] Source #

A fold with labels

eraseZip :: forall c ρ b. Forall ρ c => (forall a. c a => a -> a -> b) -> Rec ρ -> Rec ρ -> [b] Source #

A fold over two row type structures at once

eraseToHashMap :: forall c r s b. (IsString s, Eq s, Hashable s, Forall r c) => (forall a. c a => a -> b) -> Rec r -> HashMap s b Source #

Turns a record into a HashMap from values representing the labels to the values of the record.

Zip

type family Zip (r1 :: Row *) (r2 :: Row *) where ... Source #

Zips two rows together to create a Row of the pairs. The two rows must have the same set of labels.

Equations

Zip (R r1) (R r2) = R (ZipR r1 r2) 

zip :: forall r1 r2. BiForall r1 r2 Unconstrained2 => Rec r1 -> Rec r2 -> Rec (Zip r1 r2) Source #

Zips together two records that have the same set of labels.

Sequence

sequence :: forall f r. (Forall r Unconstrained1, Applicative f) => Rec (Map f r) -> f (Rec r) Source #

Applicative sequencing over a record.

sequence' :: forall f r c. (Forall r c, Applicative f) => Rec (Map f r) -> f (Rec r) Source #

A version of sequence in which the constraint for Forall can be chosen.

Compose

We can easily convert between mapping two functors over the types of a row and mapping the composition of the two functors. The following two functions perform this composition with the gaurantee that:

>>> compose . uncompose = id
>>> uncompose . compose = id

compose :: forall (f :: * -> *) (g :: * -> *) r. Forall r Unconstrained1 => Rec (Map f (Map g r)) -> Rec (Map (Compose f g) r) Source #

Convert from a record where two functors have been mapped over the types to one where the composition of the two functors is mapped over the types.

uncompose :: forall (f :: * -> *) (g :: * -> *) r. Forall r Unconstrained1 => Rec (Map (Compose f g) r) -> Rec (Map f (Map g r)) Source #

Convert from a record where the composition of two functors have been mapped over the types to one where the two functors are mapped individually one at a time over the types.

compose' :: forall c (f :: * -> *) (g :: * -> *) (r :: Row *). Forall r c => Rec (Map f (Map g r)) -> Rec (Map (Compose f g) r) Source #

A version of compose in which the constraint for Forall can be chosen.

uncompose' :: forall c (f :: * -> *) (g :: * -> *) r. Forall r c => Rec (Map (Compose f g) r) -> Rec (Map f (Map g r)) Source #

A version of uncompose in which the constraint for Forall can be chosen.

Labels

labels :: forall ρ c s. (IsString s, Forall ρ c) => [s] Source #

Return a list of the labels in a row type.

labels' :: forall ρ s. (IsString s, Forall ρ Unconstrained1) => [s] Source #

Return a list of the labels in a row type and is specialized to the Unconstrained1 constraint.

UNSAFE operations

unsafeRemove :: KnownSymbol l => Label l -> Rec r -> Rec (r .- l) Source #

Removes a label from the record but does not remove the underlying value.

This is faster than regular record removal (.-) but should only be used when either: the record will never be merged with another record again, or a new value will soon be placed into the record at this label (as in, an update that is split over two commands).

If the resulting record is then merged (with .+) with another record that contains a value at that label, an "impossible" error will occur.

unsafeInjectFront :: KnownSymbol l => Label l -> a -> Rec (R r) -> Rec (R ((l :-> a) ': r)) Source #

A helper function for unsafely adding an element to the front of a record. This can cause the resulting record to be malformed, for instance, if the record already contains labels that are lexicographically before the given label. Realistically, this function should only be used when writing calls to metamorph.