ruin-0.1.0.0: Pliable records

Safe HaskellNone
LanguageHaskell2010

Data.Ruin

Contents

Description

TH splice

The makeRecords splice reifies any data type declared with record syntax into instances of this library's classes.

  data XY y = MkXY {x :: Int, y :: y} deriving (Generic,Show)
  data YX x = MkYX {y :: Bool, x :: x} deriving (Generic,Show)

  $(makeRecords [''XY,''YX])
  -- or $(makeRecords ['MkXY,'MkYX]) would also work

Either $(makeRecords [''XY]) or $(makeRecords ['MkXY]) splices in instances that reify the record syntax declaration of MkXY into instances of the ruin package's classes. Naming the constructor lets you reify a data family instance. The generated declarations defer to the GHC.Generics defaults as much as possible.

The field names are used exactly, so use -XDuplicateRecordFields so that the automatic conversions work.

Singleton records

The ruin library also supports anonymous record types.

The :@ newtype is the singleton record type.

  *> :t dub
  Label s -> a -> s :@ a

  *> :t dub #x   -- This uses -XOverloadedLabels.
  a -> "x" :@ a

  *> :t undub #z
  "z" :@ a -> a

And a tuple of record types is also a record type if the component record types do not have any fields with the same name. Currently it supports up to 8 tuple components. Note that you can nest them if you need more!

Projection

The Has class provides the extricate projection, which allows careful control of strictness.

  *> :t runEval . extricate #x
  Has "x" t => t -> FieldType "x" t

extricate can navigate nested records with intuitive syntax.

  *> :t runEval . extricate (#x . #y)
  (Has "y" (FieldType "x" t), Has "x" t) =>
   t -> FieldType "y" (FieldType "x" t)

Conversion

The Build and IsSubtypeOf constraints provide the rup upcast with respect to record types' width subtyping relationship. IsSubtypeOf and rup essentially delegate to Has and extricate for each necessary field.

  *> let (y,z) = rup (dub #z (), MkXY {x=undefined,y="ash"})
  *> (undub #y y,undub #z z)
  ("ash",())

The rsym isomorphism is rup with a specialized type requiring that the two types be subtypes of one another.

  *> let (y,z,x) = rsym (dub #z (), MkXY {x=1,y="ash"})
  *> (undub #x x,undub #y y,undub #z z)
  (1,"ash",())

Ascription

The hoid function is a family of identity functions, indexed by types of any order. It let's you ascribe types without having to fully apply them, which is often useful for polymorphic record types.

  *> :t hoid @XY
  XY t -> XY t

  *> :t hoid @(->)
  (t -> t1) -> t -> t1

Record types have a notion of shape; see Shape for details. The UnifyShape constraint and the asShapeOf ascription can both be used to drive type inference. There are some combinators whose types are very unweildy until the involved record types' shapes are fixed.

The "complement" of a record type's shape is roughly the types of the record type's fields. The UnifyFieldTypes constraint and the asFieldTypesOf combinator support ascribing just that.

  *> :t \x y rc -> (dub #x x,dub #y y) `asFieldTypesOf` rc
  FieldType "x" rc
  -> FieldType "y" rc
  -> proxy rc
  -> ("x" :@ FieldType "x" rc, "y" :@ FieldType "y" rc)

Note that the second argument must have at least the fields of the first argument, but may have a different shape, which in particular means it may have "extra" fields.

You'll generally use the hoidProxy and proxyOf combinators to create the second argument of asFieldTypesOf.

  *> :t hoidProxy @XY
  Data.Proxy.Proxy (XY t)
  *> :t proxyOf
  a -> Data.Proxy.Proxy a

Directed conversion

The rfrom and rto combinators are rsym but additionally require an explicit type argument (like hoid) so that they read well.

  *> :t (\(x,y) -> (undub #x x,undub #y y)) . rfrom @XY
  XY t -> (Int,t)

  *> rto @XY (dub #x 1,dub #y False)
  XY {x = 1, y = False}

Quasiquoter

The rna quasiquoter enables named arguments for functions.

  *> :t \[rna|x y|] -> x * x + 3 x * y - 2 * y * y
  Num a => ("x" :@ a,"y" :@ a) -> a

It can also create anonymous records.

  *> :t \x y -> [rna|x y|]
  a -> a1 -> ("x" :@ a, "y" :@ a1)

There are some usefuls syntactic sugars; see rna for details.

  *> :t [rna| id@x show@y |]
  Show a1 => ("x" :@ (a -> a), "y" :@ (a1 -> String))

  *> :t \_x' _y' -> [rna| XY (_...') x y |]
  Int -> y -> XY y

Suppressing fields

The lopsided combinator <@ allows for left-biased field overlap.

  *> let xy = (dub #x 1, dub #y False)
  *> let yz = (dub #y 4, dub #z undefined)
  *> let f [rna|x y|] = x + y
  *> f $ rsym $ yz <@ xy
  5

The hide combinator hides some fields, without having to replace to them.

  *> :t extricate #x $ hide #x $ dub #x True
  <interactive>:1:1: error:
      * ruin: The field `x' is hidden in the type
            "x" :@ Bool
      * ...
  *> runEval $ extricate #x $ hide #y $ dub #x True
  True

You can hide multiple fields at once:

  *> :t hide (#x . #y)
  rc -> Hide '["x", "y"] rc
  True

Note that that hides the x field and the y field --- it doesn't hide a nested field x.y.

Partitioning records

Sometimes suppressing a field isn't enough, and you need to actually remove it. In that case, use the partitioning combinators.

The rdrop combinator is a stronger version of hide; given a list of labels and a record, it creates an anonymous record with the fields of the given record other than the given labels.

  *> rdrop (#x . #y) (dub #x 'x',dub #y 'y',dub #z 'z')
  MkTup1 (dub #z 'z')

Instead of listing those labels explicitly, you can use fieldLabelsOf to take them from another known record type.

Note that the rsym combinator can split a record type into two other record types that fully partition the full origial.

  data AB a b = {a::a,b::b} deriving (Generic,Show)
  data CD c d = {c::c,d::d} deriving (Generic,Show)
  $(makeRecords [''AB,''CD])

  *> hoid @AB *** hoid @CD $ rsym [rna|mempty@a mempty@b mempty@c mempty@d|]
  (Monoid a3, Monoid a2, Monoid a1, Monoid a) =>
  t -> (AB a2 a3, CD a a1)

The rtake combinator is similar, except it completely infers the type of the second component; specifically, the second component is an anonymous record type whose fields are those that are "leftover" from creating the first component. Otherwise, it's just like rsym.

Custom errors

Most of the error messages are easy to read.

  *> (\[rna|x z|] -> x + z) $ rsym (dub #x 1, dub #y 2)
  <interactive>:3:1: error:
    * ruin: Could not find a field `z' in the type
          "x" :@ t
        or in the type
          "y" :@ a
    * ...

Fieldwise combinators

Record types support an interface very similar to Applicative functors, based on fieldwise operations.

The rpure, rmonopure, rmap, and rsplat combinators are designed to mimic the familiar pure, <$>, and <*> combinators.

  *> let isZero x = 0 == x
  *> [rna|show succ pred isZero|] `rsplat` rmonopure (4 :: Int)
  (dub #show "4",dub #succ 5,dub #pred 3,dub #isZero False)

Others: rmempty, rmappend, and rlabel. See this section for more information.

Applicative variants

Many combinators have variants that work in an Applicative functor. In particular, the rnaA quasiquoter only works for expressions, and it builds records in an Applicative functor, with the effects of each field ordered as in the quasiquoter text.

  *> let x = [1,2]
  *> let y = ["y1","y2"]
  *> mapM_ print [rnaA|XY x y|]
  MkXY {x = 1,y = "y1"}
  MkXY {x = 1,y = "y2"}
  MkXY {x = 2,y = "y1"}
  MkXY {x = 2,y = "y2"}
  *> mapM_ print [rnaA|XY y x|]
  MkXY {x = 1,y = "y1"}
  MkXY {x = 2,y = "y1"}
  MkXY {x = 1,y = "y2"}
  MkXY {x = 2,y = "y2"}

Others: rfromA, rsymA, rtoA, rupA, rmapA, and rsplatA.

Synopsis

Singleton records

data s :@ a infix 1 Source #

A record type with a single field.

Instances

(~) Symbol s1 s2 => Has s1 ((:@) s2 a) Source # 

Associated Types

type FieldType (s1 :: Symbol) ((:@) s2 a) :: * Source #

Methods

extricate1 :: Label s1 -> (s2 :@ a) -> Eval (FieldType s1 (s2 :@ a)) Source #

Functor ((:@) s) Source # 

Methods

fmap :: (a -> b) -> (s :@ a) -> s :@ b #

(<$) :: a -> (s :@ b) -> s :@ a #

Generic1 ((:@) s) Source # 

Associated Types

type Rep1 ((:@) s :: * -> *) :: * -> * #

Methods

from1 :: (s :@ a) -> Rep1 ((:@) s) a #

to1 :: Rep1 ((:@) s) a -> s :@ a #

Eq a => Eq ((:@) s a) Source # 

Methods

(==) :: (s :@ a) -> (s :@ a) -> Bool #

(/=) :: (s :@ a) -> (s :@ a) -> Bool #

(Data a, KnownSymbol s) => Data ((:@) s a) Source # 

Methods

gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> (s :@ a) -> c (s :@ a) #

gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (s :@ a) #

toConstr :: (s :@ a) -> Constr #

dataTypeOf :: (s :@ a) -> DataType #

dataCast1 :: Typeable (* -> *) t => (forall d. Data d => c (t d)) -> Maybe (c (s :@ a)) #

dataCast2 :: Typeable (* -> * -> *) t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c (s :@ a)) #

gmapT :: (forall b. Data b => b -> b) -> (s :@ a) -> s :@ a #

gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> (s :@ a) -> r #

gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> (s :@ a) -> r #

gmapQ :: (forall d. Data d => d -> u) -> (s :@ a) -> [u] #

gmapQi :: Int -> (forall d. Data d => d -> u) -> (s :@ a) -> u #

gmapM :: Monad m => (forall d. Data d => d -> m d) -> (s :@ a) -> m (s :@ a) #

gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> (s :@ a) -> m (s :@ a) #

gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> (s :@ a) -> m (s :@ a) #

Ord a => Ord ((:@) s a) Source # 

Methods

compare :: (s :@ a) -> (s :@ a) -> Ordering #

(<) :: (s :@ a) -> (s :@ a) -> Bool #

(<=) :: (s :@ a) -> (s :@ a) -> Bool #

(>) :: (s :@ a) -> (s :@ a) -> Bool #

(>=) :: (s :@ a) -> (s :@ a) -> Bool #

max :: (s :@ a) -> (s :@ a) -> s :@ a #

min :: (s :@ a) -> (s :@ a) -> s :@ a #

(KnownSymbol s, Show a) => Show ((:@) s a) Source # 

Methods

showsPrec :: Int -> (s :@ a) -> ShowS #

show :: (s :@ a) -> String #

showList :: [s :@ a] -> ShowS #

Generic ((:@) s a) Source # 

Associated Types

type Rep ((:@) s a) :: * -> * #

Methods

from :: (s :@ a) -> Rep (s :@ a) x #

to :: Rep (s :@ a) x -> s :@ a #

(Lift a, KnownSymbol s) => Lift ((:@) s a) Source # 

Methods

lift :: (s :@ a) -> Q Exp #

Binary a => Binary ((:@) s a) Source # 

Methods

put :: (s :@ a) -> Put #

get :: Get (s :@ a) #

putList :: [s :@ a] -> Put #

Serialize a => Serialize ((:@) s a) Source # 

Methods

put :: Putter (s :@ a) #

get :: Get (s :@ a) #

NFData a => NFData ((:@) s a) Source # 

Methods

rnf :: (s :@ a) -> () #

Build ((:@) s a) Source # 

Associated Types

type Fields ((:@) s a) :: [(Symbol, *)] Source #

type Shape ((:@) s a) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (s :@ a)) i rc) => rc -> Compose * * Eval i (s :@ a) Source #

buildNonStrict :: GivesThese (Fields (s :@ a)) Identity rc => rc -> s :@ a Source #

type FieldType s1 ((:@) s2 a) Source # 
type FieldType s1 ((:@) s2 a) = a
type Rep1 ((:@) s) Source # 
type Rep1 ((:@) s) = D1 (MetaData ":@" "Data.Ruin.All" "ruin-0.1.0.0-S5TDo3Tpc1FUEQd4hVBnP" True) (C1 (MetaCons "Dub" PrefixI False) (S1 (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) Par1))
type Rep ((:@) s a) Source # 
type Rep ((:@) s a) = D1 (MetaData ":@" "Data.Ruin.All" "ruin-0.1.0.0-S5TDo3Tpc1FUEQd4hVBnP" True) (C1 (MetaCons "Dub" PrefixI False) (S1 (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 a)))
type Fields ((:@) s a) Source # 
type Fields ((:@) s a) = (:) (Symbol, *) ((,) Symbol * s a) ([] (Symbol, *))
type Shape ((:@) s _) o Source # 
type Shape ((:@) s _) o = Hoid * (* -> *) ((:@) s) o

dub :: Label s -> a -> s :@ a Source #

undub :: Label s -> (s :@ a) -> a Source #

Accessing parts of records

class Has s t where Source #

t has a field named s that inhabits FieldType s t.

extricate1 projects out the field, with special care to strictness. The Eval layer provides a stopping point for the projection computation. Without this layer, one would have to force the value itself in order to force the extrication enough so that the rest of t could be GC'd. On the contrary, case extricate1 t of Done x -> x neither retains t nor forces x.

Strictness
Forcing the Done layer of extricate1 forces enough of t to reach the field but doesn't force the field. This is difficult to formalize in a general and illuminating way, so this law is instantiated below for a simple record type.
  data XY = MkXY {x,y :: Int}

  extricate1 #x (undefined :: XY) = undefined

  flip seq () $ extricate1 #x (MkXY undefined undefined) = ()

Associated Types

type FieldType s t :: * Source #

Methods

extricate1 :: Label s -> t -> Eval (FieldType s t) Source #

extricate1 :: (Generic t, GBox (IsABox (Rep t)) t, GenericHas (Rep t) s (FieldType s t)) => Label s -> t -> Eval (FieldType s t) Source #

Instances

TypeError Constraint (NoSuchField s ()) => Has s () Source #

This is a non-instance.

Associated Types

type FieldType (s :: Symbol) () :: * Source #

Methods

extricate1 :: Label s -> () -> Eval (FieldType s ()) Source #

Has s a => Has s (Tup1 a) Source # 

Associated Types

type FieldType (s :: Symbol) (Tup1 a) :: * Source #

Methods

extricate1 :: Label s -> Tup1 a -> Eval (FieldType s (Tup1 a)) Source #

ClosedHas s (R fd) => Has s (R fd) Source # 

Associated Types

type FieldType (s :: Symbol) (R fd) :: * Source #

Methods

extricate1 :: Label s -> R fd -> Eval (FieldType s (R fd)) Source #

(DisjointFields a b, Has s (Pair a b)) => Has s (a, b) Source # 

Associated Types

type FieldType (s :: Symbol) (a, b) :: * Source #

Methods

extricate1 :: Label s -> (a, b) -> Eval (FieldType s (a, b)) Source #

(~) Symbol s1 s2 => Has s1 ((:@) s2 a) Source # 

Associated Types

type FieldType (s1 :: Symbol) ((:@) s2 a) :: * Source #

Methods

extricate1 :: Label s1 -> (s2 :@ a) -> Eval (FieldType s1 (s2 :@ a)) Source #

TypeError Constraint (NoFun (Symbol -> * -> Constraint) Has) => Has s (a -> b) Source #

This is a non-instance.

Associated Types

type FieldType (s :: Symbol) (a -> b) :: * Source #

Methods

extricate1 :: Label s -> (a -> b) -> Eval (FieldType s (a -> b)) Source #

MightHave (Pair l r) (Find (Pair l r) s) (Pair l r) s (FieldType s (Pair l r)) => Has s (Pair l r) Source # 

Associated Types

type FieldType (s :: Symbol) (Pair l r) :: * Source #

Methods

extricate1 :: Label s -> Pair l r -> Eval (FieldType s (Pair l r)) Source #

Has_Hide (Elem Symbol s sHiddens) s rc => Has s (Hide sHiddens rc) Source # 

Associated Types

type FieldType (s :: Symbol) (Hide sHiddens rc) :: * Source #

Methods

extricate1 :: Label s -> Hide sHiddens rc -> Eval (FieldType s (Hide sHiddens rc)) Source #

Has s ((a, b), c) => Has s (a, b, c) Source # 

Associated Types

type FieldType (s :: Symbol) (a, b, c) :: * Source #

Methods

extricate1 :: Label s -> (a, b, c) -> Eval (FieldType s (a, b, c)) Source #

Has s ((a, b), (c, d)) => Has s (a, b, c, d) Source # 

Associated Types

type FieldType (s :: Symbol) (a, b, c, d) :: * Source #

Methods

extricate1 :: Label s -> (a, b, c, d) -> Eval (FieldType s (a, b, c, d)) Source #

Has s ((a, b, c), (d, e)) => Has s (a, b, c, d, e) Source # 

Associated Types

type FieldType (s :: Symbol) (a, b, c, d, e) :: * Source #

Methods

extricate1 :: Label s -> (a, b, c, d, e) -> Eval (FieldType s (a, b, c, d, e)) Source #

Has s ((a, b, c), (d, e, f)) => Has s (a, b, c, d, e, f) Source # 

Associated Types

type FieldType (s :: Symbol) (a, b, c, d, e, f) :: * Source #

Methods

extricate1 :: Label s -> (a, b, c, d, e, f) -> Eval (FieldType s (a, b, c, d, e, f)) Source #

Has s ((a, b, c, d), (e, f, g)) => Has s (a, b, c, d, e, f, g) Source # 

Associated Types

type FieldType (s :: Symbol) (a, b, c, d, e, f, g) :: * Source #

Methods

extricate1 :: Label s -> (a, b, c, d, e, f, g) -> Eval (FieldType s (a, b, c, d, e, f, g)) Source #

Has s ((a, b, c, d), (e, f, g, h)) => Has s (a, b, c, d, e, f, g, h) Source # 

Associated Types

type FieldType (s :: Symbol) (a, b, c, d, e, f, g, h) :: * Source #

Methods

extricate1 :: Label s -> (a, b, c, d, e, f, g, h) -> Eval (FieldType s (a, b, c, d, e, f, g, h)) Source #

extricate :: forall ss t. DeepHas ss t => Labels ss -> t -> Eval (DeepFieldType ss t) Source #

extricate project a field out of nested records by iterating extricate1.

The first argument is a function type so that the syntax can use . to specify a sequence of labels.

  extricate id = return

  extricate (#s . ss) = extricate1 #s Control.Monad.>=> extricate ss

rna :: QuasiQuoter Source #

Named arguments for functions.

  (\[rna|x] -> x) :: Tup1 ("x" :@ a) -> a

  (\[rna|x y] -> (x,y)) :: ("x" :@ a,"y" :@ b) -> (a,b)

  (\[rna|y x] -> (x,y)) :: ("y" :@ b,"x" :@ a) -> (a,b)

And so on. The Has and Build classes support such tuples up to 8 components.

There are four pieces of special syntax, none of which can be escaped.

  • A @ allows a different variable name than the field name.

      (\f [rna|l@x|] [rna|r@x|] -> f l r)
        :: (a -> b -> c) -> Tup1 ("x" :@ a) -> Tup1 ("x" :@ b) -> c
    
  • A name of _ is a wildcard pattern.
  • A leading word of the form (<prefix>...<suffix>) adds a prefix and/or suffix to all of the variable names. This affects even the names given with @ syntax. It does not affect wildcards.

      (\[rna|(...L) x y|] [rna|(r_...') x y|] -> xL == r_x' && yL == r_y')
        :: (Eq a,Eq b) => ("x" :@ a,"y" :@ b) -> ("x" :@ a,"y" :@ b) -> Bool
    
  • A ! at the beginning of the pattern makes it strict.

      -- strict
      (\[rna|!x|] -> Just x) :: Tup1 ("x" :@ a) -> Maybe a
    
      -- strict
      (\[rna|!x@foo|] -> Just x) :: Tup1 ("foo" :@ a) -> Maybe a
    
      -- strict
      (\[rna|!x@!|] -> Just x) :: Tup1 ("!" :@ a) -> Maybe a
    
      -- not strict
      (\[rna|x@!|] -> Just x) :: Tup1 ("!" :@ a) -> Maybe a
    

    Note a ~ pattern for binding would be redundant, since the bindings are ultimately variable bindings. Though it may be useful to apply a tilde pattern to the entire quasiquote.

  • A leading word that is capitalized is interpreted as the name of a record type and is ascribed via rfrom.
     data XY x y = MkXY {x :: x,y :: y}

     (\[rna| XY x y|] -> (x,y)) :: XY t t1 -> (t, t1)
   

When both are present, the type name must precede the prefix and/or suffix.

rna also works as an expression. All of the sugar except wildcards is supported in the dual way.

rnaA :: QuasiQuoter Source #

rnaA is like rna, but:

  • it only works for expressions,
  • it only works inside an Applicative.

Hiding fields

data Hide ss rc Source #

Deny the Has instance for each of ss.

Instances

Has_Hide (Elem Symbol s sHiddens) s rc => Has s (Hide sHiddens rc) Source # 

Associated Types

type FieldType (s :: Symbol) (Hide sHiddens rc) :: * Source #

Methods

extricate1 :: Label s -> Hide sHiddens rc -> Eval (FieldType s (Hide sHiddens rc)) Source #

type FieldType s (Hide sHiddens rc) Source # 
type FieldType s (Hide sHiddens rc) = FieldType s rc

hide :: Labels ss -> rc -> Hide ss rc Source #

Deny ("forget") a Has s instance.

Record types' width subtyping

Lowest-level combinators

class Build t where Source #

Record types: product types where each factor has a static name (i.e. the Fields).

Minimal complete definition

build

Associated Types

type Fields (t :: *) :: [(Symbol, *)] Source #

Each element of this list is the name of a field and its type in t. Default: GenericFields.

Unique Names
These fields have different names.
  NubByFst (Fields t) = Fields t
Partitioning
These fields partition t.
  t is isomorphic to a tuple of the types MapSnd (Fields t)

type Shape (t :: *) (o :: *) :: Constraint Source #

The shape of a record type is its most general type, the one that all instances of that record type are specializations of. Unless you're being clever, the shape of the type class index t is t, since that t is usually as polymorphic as it could be (i.e. the value that the type variables within t take on do not change how it instantiates Build).

The Shape t o constraint requires --- via ~ --- that o has the same shape as t. It must use ~ to assert this requirement, so that it can guide type inference.

Shape defaults to GenericShape, which is correct for data types declared with record syntax except for data family instances. See GenericShape for more info.

Methods

build :: (Applicative i, GivesThese (Fields t) i rc) => rc -> Compose Eval i t Source #

The laws for build are given without loss of generality in terms of rupEval.

Eta
An eta rule.
  t <$ rupEval t = rupEval t

This law reasonably requires that t Has all of its own Fields.

Strictness
The rupEval function is strict in its argument, but it's only strict enough to retrieve the thunks for each of the necessary fields, without forcing those thunks.
  seq (rupEval rc) =
      seq (extricate1 #f1 rc)
    . seq (extricate1 #f2 rc)
    ...
    . seq (extricate1 #fN rc)

If rc is a typical single-constructor record type declared with record syntax and has fields for all of t's Fields, then the Strictness law simplifies to seq (rupEval rc) = seq rc.

Note
A GHC.Generics default is available as genericBuild. We do not provide a DefaultSignature because it is most often critical for performance that build is inlined, which requires an explicit INLINE pragma (the RHS size gets too large for inferred inlining with even just three fields). We thus recommend the following.
  instance Build Foo where
    {-# INLINE build #-}
    build = genericBuild

buildNonStrict :: GivesThese (Fields t) Identity rc => rc -> t Source #

Like build, but maximally non-strict instead of having the Strictness law. Defaults to genericBuildNonStrict, but beware that a manual INLINE pragma is likely as useful as it is for build.

      seq (build rc) (buildNonStrict rc) = runEval (build rc)
    

buildNonStrict :: (Fields t ~ GenericFields t, Generic t, GenericBuild t (Rep t), GivesThese (Fields t) Identity rc) => rc -> t Source #

Like build, but maximally non-strict instead of having the Strictness law. Defaults to genericBuildNonStrict, but beware that a manual INLINE pragma is likely as useful as it is for build.

      seq (build rc) (buildNonStrict rc) = runEval (build rc)
    

Instances

Build () Source # 

Associated Types

type Fields () :: [(Symbol, *)] Source #

type Shape () o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields ()) i rc) => rc -> Compose * * Eval i () Source #

buildNonStrict :: GivesThese (Fields ()) Identity rc => rc -> () Source #

Build a => Build (Tup1 a) Source # 

Associated Types

type Fields (Tup1 a) :: [(Symbol, *)] Source #

type Shape (Tup1 a) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (Tup1 a)) i rc) => rc -> Compose * * Eval i (Tup1 a) Source #

buildNonStrict :: GivesThese (Fields (Tup1 a)) Identity rc => rc -> Tup1 a Source #

(KnownFD fd, Hoid FD ([(Symbol, *)] -> FD) MkFD fd) => Build (R fd) Source # 

Associated Types

type Fields (R fd) :: [(Symbol, *)] Source #

type Shape (R fd) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (R fd)) i rc) => rc -> Compose * * Eval i (R fd) Source #

buildNonStrict :: GivesThese (Fields (R fd)) Identity rc => rc -> R fd Source #

TypeError Constraint (NoFun (* -> Constraint) Build) => Build (a -> b) Source #

This is a non-instance.

Associated Types

type Fields (a -> b) :: [(Symbol, *)] Source #

type Shape (a -> b) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (a -> b)) i rc) => rc -> Compose * * Eval i (a -> b) Source #

buildNonStrict :: GivesThese (Fields (a -> b)) Identity rc => rc -> a -> b Source #

(DisjointFields a b, Lemma_AppendGivesThese (Fields a), Build a, Build b) => Build (a, b) Source # 

Associated Types

type Fields (a, b) :: [(Symbol, *)] Source #

type Shape (a, b) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (a, b)) i rc) => rc -> Compose * * Eval i (a, b) Source #

buildNonStrict :: GivesThese (Fields (a, b)) Identity rc => rc -> (a, b) Source #

Build ((:@) s a) Source # 

Associated Types

type Fields ((:@) s a) :: [(Symbol, *)] Source #

type Shape ((:@) s a) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (s :@ a)) i rc) => rc -> Compose * * Eval i (s :@ a) Source #

buildNonStrict :: GivesThese (Fields (s :@ a)) Identity rc => rc -> s :@ a Source #

TypeError Constraint NoBuildPair => Build (Pair l r) Source #

This is a non-instance.

Associated Types

type Fields (Pair l r) :: [(Symbol, *)] Source #

type Shape (Pair l r) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (Pair l r)) i rc) => rc -> Compose * * Eval i (Pair l r) Source #

buildNonStrict :: GivesThese (Fields (Pair l r)) Identity rc => rc -> Pair l r Source #

Build ((a, b), c) => Build (a, b, c) Source # 

Associated Types

type Fields (a, b, c) :: [(Symbol, *)] Source #

type Shape (a, b, c) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (a, b, c)) i rc) => rc -> Compose * * Eval i (a, b, c) Source #

buildNonStrict :: GivesThese (Fields (a, b, c)) Identity rc => rc -> (a, b, c) Source #

Build ((a, b), (c, d)) => Build (a, b, c, d) Source # 

Associated Types

type Fields (a, b, c, d) :: [(Symbol, *)] Source #

type Shape (a, b, c, d) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (a, b, c, d)) i rc) => rc -> Compose * * Eval i (a, b, c, d) Source #

buildNonStrict :: GivesThese (Fields (a, b, c, d)) Identity rc => rc -> (a, b, c, d) Source #

Build ((a, b, c), (d, e)) => Build (a, b, c, d, e) Source # 

Associated Types

type Fields (a, b, c, d, e) :: [(Symbol, *)] Source #

type Shape (a, b, c, d, e) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (a, b, c, d, e)) i rc) => rc -> Compose * * Eval i (a, b, c, d, e) Source #

buildNonStrict :: GivesThese (Fields (a, b, c, d, e)) Identity rc => rc -> (a, b, c, d, e) Source #

Build ((a, b, c), (d, e, f)) => Build (a, b, c, d, e, f) Source # 

Associated Types

type Fields (a, b, c, d, e, f) :: [(Symbol, *)] Source #

type Shape (a, b, c, d, e, f) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (a, b, c, d, e, f)) i rc) => rc -> Compose * * Eval i (a, b, c, d, e, f) Source #

buildNonStrict :: GivesThese (Fields (a, b, c, d, e, f)) Identity rc => rc -> (a, b, c, d, e, f) Source #

Build ((a, b, c, d), (e, f, g)) => Build (a, b, c, d, e, f, g) Source # 

Associated Types

type Fields (a, b, c, d, e, f, g) :: [(Symbol, *)] Source #

type Shape (a, b, c, d, e, f, g) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (a, b, c, d, e, f, g)) i rc) => rc -> Compose * * Eval i (a, b, c, d, e, f, g) Source #

buildNonStrict :: GivesThese (Fields (a, b, c, d, e, f, g)) Identity rc => rc -> (a, b, c, d, e, f, g) Source #

Build ((a, b, c, d), (e, f, g, h)) => Build (a, b, c, d, e, f, g, h) Source # 

Associated Types

type Fields (a, b, c, d, e, f, g, h) :: [(Symbol, *)] Source #

type Shape (a, b, c, d, e, f, g, h) o :: Constraint Source #

Methods

build :: (Applicative i, GivesThese (Fields (a, b, c, d, e, f, g, h)) i rc) => rc -> Compose * * Eval i (a, b, c, d, e, f, g, h) Source #

buildNonStrict :: GivesThese (Fields (a, b, c, d, e, f, g, h)) Identity rc => rc -> (a, b, c, d, e, f, g, h) Source #

Pure combinators

(<@) :: l -> r -> Pair l r infixr 3 Source #

Combine two types that might have Has instances for the same Symbol s. Has on the result will prefer the first argument.

NOTE WELL: Pairs are not record types. They only have Has instances.

rdrop :: (rc ~ Rcrd (DifferenceByFst (Fields t) fs), Build rc, t `IsSubtypeOf` rc) => Labels fs -> t -> rc Source #

Create an anonymous record that contains the fields of t that are not named in fs.

rfrom :: forall h rc t. (Hoid h rc, rc `IsSymmetricRecordOf` t, Build t) => rc -> t Source #

rfrom @h = rsym . hoid @h

rsym :: (l `IsSymmetricRecordOf` r, Build r) => l -> r Source #

An isomorphism based on rup, when the two record types have a symmetric subtyping relation.

Isomorphism
  forall t s.
    ( s 'IsSubtypeOf' t,Build s
    , t 'IsSubtypeOf' s,Build t
    ) => rsym . id @t . rsym = id @s

rtake :: (leftovers ~ Rcrd (DifferenceByFst (Fields t) (FieldNames taken)), Build (taken, leftovers), t `IsSymmetricRecordOf` (taken, leftovers)) => t -> (taken, leftovers) Source #

Split a record into two separate types, where the second type is an anonymous record defined as the leftovers from the first type.

rto :: forall h t rc. (Hoid h t, rc `IsSymmetricRecordOf` t, Build t) => rc -> t Source #

rto @h = hoid @h . rsym

rup :: forall t rc. (Build t, rc `IsSubtypeOf` t) => rc -> t Source #

rup is an upcast with respect to the width subtyping relationship of records; it builds a t from any type that has all of t's Fields.

Applicative combinators

rfromA :: forall h rc t i. (Hoid h rc, Applicative i, SymmetricRecordsA t i rc, Build t) => rc -> i t Source #

rsymA :: (Applicative i, SymmetricRecordsA t i rc, Build t) => rc -> i t Source #

rtoA :: forall h t rc i. (Hoid h t, Applicative i, SymmetricRecordsA t i rc, Build t) => rc -> i t Source #

rupA :: forall t rc i. (Applicative i, Build t, GivesThese (Fields t) i (GiveAllItHasA rc)) => rc -> i t Source #

rup is an upcast with respect to the width subtyping relationship of records; it builds a t from any type that has all of t's Fields.

Fieldwise combinators

The types of these combinators are not useful in the abstract. However, once the Shape of any argument record type or result record type is fixed, the types reduce to something plain.

  *> :t rsplat [rna|show@x id@y|]
  Show t => ("x" :@ t, "y" :@ t1) -> ("x" :@ String, "y" :@ t1)

A basic, self-contained example:

  data PrintAndTime = MkPrintAndTime

  instance (Show a,f ~ (a -> IO (a,Integer))) => FPure PrintAndTime s f where
    fpure _ x = do
      print x
      (,) x <$> System.CPUTime.getCPUTime

  *> rmapA MkPrintAndTime (dub #x "OK", dub #y ())
  "OK"
  ()
  (dub #x ("OK",43062500000000),dub #y ((),43062500000000))

class FPure a s b where Source #

How to create a field s of type b from a value of a.

Minimal complete definition

fpure

Methods

fpure :: a -> b Source #

Instances

(Ord a, (~) * b (a -> a -> Ordering)) => FPure RCompare s b Source # 

Methods

fpure :: RCompare -> b Source #

(Eq a, (~) * b (a -> a -> All)) => FPure REq s b Source # 

Methods

fpure :: REq -> b Source #

(KnownSymbol s, Show a, (~) * b (a -> [String])) => FPure RShowField s b Source # 

Methods

fpure :: RShowField -> b Source #

(~) * b (dom -> cod) => FPure (dom -> cod) s b Source #

Same as rmonopure.

Methods

fpure :: (dom -> cod) -> b Source #

rlabel :: (Build t, GivesThese (Fields t) Identity (RPure RLabel)) => t Source #

The record where the type of field s is Label s.

  > :t hoid @((:@) "x") rlabel
  hoid @((:@) "x") rlabel :: "x" :@ Label "x"

rmempty :: (Build t, GivesThese (Fields t) Identity (RPure RMEmpty)) => t Source #

A record where every field is mempty.

  > :t hoid @((:@) "x") rmempty
  hoid @((:@) "x") rmempty :: Monoid t => "x" :@ t

rmap :: forall fun rc rfun t. (Build rfun, Build t, GivesThese (Fields rfun) Identity (RPure fun), GivesThese (Fields t) Identity (RSplat rfun rc), UnifyShape rfun t, UnifyShape rc t) => fun -> rc -> t infixl 4 Source #

If the following constraint holds for every field s in t, then fun can map rc to t.

  FPure fun s (FieldType s rc -> FieldType s t)
  > :t \fun -> rmap fun . hoid @((:@) "x")
  \fun -> rmap fun . hoid @((:@) "x")
    :: FPure fun "x" (t -> t1) => fun -> "x" :@ t -> "x" :@ t1

rmapA :: forall fun i rc rfun t. (Applicative i, Build rfun, Build t, GivesThese (Fields rfun) Identity (RPure fun), GivesThese (Fields t) i (RSplatA rfun rc), UnifyShape rfun t, UnifyShape rc t) => fun -> rc -> i t infixl 4 Source #

If the following constraint holds for every field s in t, then fun can map rc to t within an Applicative functor i.

  FPure fun s (FieldType s rc -> i (FieldType s t))
  > :t \fun -> rmapA fun . hoid @((:@) "x")
  \fun -> rmapA fun . hoid @((:@) "x")
    :: (FPure fun "x" (t -> i t1), Applicative i) =>
       fun -> "x" :@ t -> i ("x" :@ t1)

rmappend :: (Build t, GivesThese (Fields t) Identity (RMAppend t t)) => t -> t -> t Source #

Combine two records if all of the fields are Monoids.

  > :t \l r -> hoid @((:@) "x") $ rmappend l r
  \l r -> hoid @((:@) "x") $ rmappend l r
    :: Monoid t => "x" :@ t -> "x" :@ t -> "x" :@ t

rmonopure :: (Build t, GivesThese (Fields t) Identity (RPure (RMonoPure a))) => a -> t Source #

A record where every field is a given monomorphic value.

  > :t hoid @((:@) "x") . rmonopure
  hoid @((:@) "x") . rmonopure :: t -> "x" :@ t

rpolypure :: (Build t, GivesThese (Fields t) Identity (RPure a)) => a -> t Source #

Alias for rpure, symmetric with rmonopure.

rpure :: (Build t, GivesThese (Fields t) Identity (RPure a)) => a -> t Source #

A record where the value of field s is fpure @a @s a, for the given a.

  > :t hoid @((:@) "x") . rpure
  hoid @((:@) "x") . rpure :: FPure a "x" t => a -> "x" :@ t

rsappend :: (Build t, GivesThese (Fields t) Identity (RSAppend t t)) => t -> t -> t Source #

Combine two records if all of the fields are Semigroupss.

  > :t \l r -> hoid @((:@) "x") $ rsappend l r
  \l r -> hoid @((:@) "x") $ rsappend l r
    :: Semigroup t => "x" :@ t -> "x" :@ t -> "x" :@ t

rsplat :: (Build t, GivesThese (Fields t) Identity (RSplat rfun rc), UnifyShape rc t, UnifyShape rfun t) => rfun -> rc -> t infixl 4 Source #

A record where the value of field s is runEval (extricate #s rfun <*> extricate #s rc).

Compare to "zippy" instances of <*>.

  > :t rsplat . hoid @((:@) "x")
  rsplat . hoid @((:@) "x")
    :: "x" :@ (t1 -> t) -> "x" :@ t1 -> "x" :@ t

rsplatA :: (Applicative i, Build t, GivesThese (Fields t) i (RSplatA rfun rc), UnifyShape rc t, UnifyShape rfun t) => rfun -> rc -> i t infixl 4 Source #

Like rsplat, but in an Applicative functor. Note that every field in rfun must be a function with an i-structured codomain.

  > :t rsplatA . hoid @((:@) "x")
  rsplatA . hoid @((:@) "x")
    Applicative i => :: "x" :@ (t1 -> i t) -> "x" :@ t1 -> i ("x" :@ t)

Ascription

type family UnifyFieldTypes (ss :: [Symbol]) (t :: *) (h :: *) :: Constraint where ... Source #

Equations

UnifyFieldTypes '[] _ _ = () 
UnifyFieldTypes (s ': ss) t h = (FieldType s t ~ FieldType s h, UnifyFieldTypes ss t h) 

type UnifyShape l r = (Shape l r, Shape r l) Source #

Unify the shape of two record types; see Shape.

asFieldTypesOf :: UnifyFieldTypes (FieldNames t) t rc => t -> proxy rc -> t Source #

asShapeOf :: UnifyShape l r => l -> r -> l Source #

Like asTypeOf, but doesn't require that the fields have the same types, only that the record types have the same shape.

hoid :: forall t a. Hoid t a => a -> a Source #

A family of identity functions indexed by possibly higher-order types. hoid @t asserts that a is either equal to t or is an application of t.

The Hoid type family vanishes if the kind of t is defined enough to fully determine the arity of t. If Hoid doesn't vanish in a use case, then hoid is not intended for use in that case.

hoidProxy :: forall t a. Hoid t a => Proxy a Source #

'hoidProxy @t' = hoid @t $ Proxy

Conveniences

data Label s Source #

Use -XOverloadedLabels to create labels. For example, #x :: Label "x".

Or use mkLabel.

Instances

(~) Symbol s1 s2 => IsLabel s1 (Label s2) Source # 

Methods

fromLabel :: Proxy# Symbol s1 -> Label s2 #

type Labels fs = Labels_ '[] -> Labels_ fs Source #

This type is an instance of a type-level difference list, so that sequences of labels can be written as #x . #y . #z :: Labels '["x","y","z"], for example.

class NoWarnUnusedTopBind t where Source #

Merely a receptacle in which the user can syntactially use a record selector to avoid the -Wunused-top-bind warning without having to export the record selector.

  {-# OPTIONS_GHC -Werror -Wall #-}

  module Foo (Bar(MkBar)) where

  data Bar = MkBar {x,y :: Int}

  instance NoWarnUnusedTopBind Bar where noWarnUnusedTopBind MkBar{x=_,y=_} = ()
  instance Has "x" Bar
  instance Has "y" Bar
  instance Build Bar where
    {-# INLINE rupEval #-}
    rupEval = genericRupEval

x and y in that example are neither exported nor really used, but there will be no warnings.

An explicit instance of NFData, for example, will often use a similar record pattern that serves to use the selectors. On the other hand, most such instances are now quite conveient to implicitly derive, so this NoWarnUnusedTopBind class may be the most obvious way to inconsequentially "use" a record selector so as to avoid the -Wunused-top-bind warning.

Minimal complete definition

noWarnUnusedTopBind

Methods

noWarnUnusedTopBind :: Hoid t a => a -> () Source #

fieldLabelsOf :: forall t proxy. proxy t -> Labels (FieldNames t) Source #

Get the labels of a record type's fields.

makeRecords :: [Name] -> Q [Dec] Source #

Declare the straight-forward Has and Build instances for a record type. A data type is a record type if it has exactly one constructor and that constructor is declared using record syntax.

An instance of a data family can be a record type; refer to that type by the name of the instance's constructor.

The generated code relies on the GHC.Generics defaults in the same way a user would; it merely relieves you from enumerating the per-field instances.

Also, the splice will declare the instances in the style of Data.Ruin.ClosedHas.

mkLabel :: forall s. Label s Source #

Creates a label that is determined either by type inference or via -XTypeApplications.

mkLabels :: forall fs. Labels fs Source #

proxyOf :: a -> Proxy a Source #

proxyOf = const Proxy