Safe Haskell | None |
---|---|
Language | Haskell2010 |
- 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 workEither
$(
ormakeRecords
[''XY])$(
splices in instances that reify the record syntax declaration ofmakeRecords
['MkXY])MkXY
into instances of theruin
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 *> :tdub
#x -- This uses -XOverloadedLabels. a -> "x":@
a *> :tundub
#z "z":@
a -> aAnd 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 theextricate
projection, which allows careful control of strictness.*> :t
runEval
.extricate
#xHas
"x" t => t ->FieldType
"x" textricate
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
andIsSubtypeOf
constraints provide therup
upcast with respect to record types' width subtyping relationship.IsSubtypeOf
andrup
essentially delegate toHas
andextricate
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 isrup
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 *> :thoid
@(->) (t -> t1) -> t -> t1Record types have a notion of shape; see
Shape
for details. TheUnifyShape
constraint and theasShapeOf
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 theasFieldTypesOf
combinator support ascribing just that.*> :t \x y rc -> (
dub
#x x,dub
#y y) `asFieldTypesOf
` rcFieldType
"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
andproxyOf
combinators to create the second argument ofasFieldTypesOf
.*> :t
hoidProxy
@XY Data.Proxy.Proxy (XY t) *> :tproxyOf
a -> Data.Proxy.Proxy a
- Directed conversion
The
rfrom
andrto
combinators arersym
but additionally require an explicit type argument (likehoid
) 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) -> aIt 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 5The
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 TrueYou can hide multiple fields at once:
*> :t
hide
(#x . #y) rc ->Hide
'["x", "y"] rc TrueNote that that hides the
x
field and they
field --- it doesn't hide a nested fieldx.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 ofhide
; 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 likersym
.
- 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
, andrsplat
combinators are designed to mimic the familiarpure
,<$>
, 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
, andrlabel
. 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"}
- data s :@ a
- dub :: Label s -> a -> s :@ a
- undub :: Label s -> (s :@ a) -> a
- class Has s t where
- extricate :: forall ss t. DeepHas ss t => Labels ss -> t -> Eval (DeepFieldType ss t)
- rna :: QuasiQuoter
- rnaA :: QuasiQuoter
- data Hide ss rc
- hide :: Labels ss -> rc -> Hide ss rc
- class Build t where
- (<@) :: l -> r -> Pair l r
- rdrop :: (rc ~ Rcrd (DifferenceByFst (Fields t) fs), Build rc, t `IsSubtypeOf` rc) => Labels fs -> t -> rc
- rfrom :: forall h rc t. (Hoid h rc, rc `IsSymmetricRecordOf` t, Build t) => rc -> t
- rsym :: (l `IsSymmetricRecordOf` r, Build r) => l -> r
- rtake :: (leftovers ~ Rcrd (DifferenceByFst (Fields t) (FieldNames taken)), Build (taken, leftovers), t `IsSymmetricRecordOf` (taken, leftovers)) => t -> (taken, leftovers)
- rto :: forall h t rc. (Hoid h t, rc `IsSymmetricRecordOf` t, Build t) => rc -> t
- rup :: forall t rc. (Build t, rc `IsSubtypeOf` t) => rc -> t
- rfromA :: forall h rc t i. (Hoid h rc, Applicative i, SymmetricRecordsA t i rc, Build t) => rc -> i t
- rsymA :: (Applicative i, SymmetricRecordsA t i rc, Build t) => rc -> i t
- rtoA :: forall h t rc i. (Hoid h t, Applicative i, SymmetricRecordsA t i rc, Build t) => rc -> i t
- rupA :: forall t rc i. (Applicative i, Build t, GivesThese (Fields t) i (GiveAllItHasA rc)) => rc -> i t
- class FPure a s b where
- rlabel :: (Build t, GivesThese (Fields t) Identity (RPure RLabel)) => t
- rmempty :: (Build t, GivesThese (Fields t) Identity (RPure RMEmpty)) => 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
- 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
- rmappend :: (Build t, GivesThese (Fields t) Identity (RMAppend t t)) => t -> t -> t
- rmonopure :: (Build t, GivesThese (Fields t) Identity (RPure (RMonoPure a))) => a -> t
- rpolypure :: (Build t, GivesThese (Fields t) Identity (RPure a)) => a -> t
- rpure :: (Build t, GivesThese (Fields t) Identity (RPure a)) => a -> t
- rsappend :: (Build t, GivesThese (Fields t) Identity (RSAppend t t)) => t -> t -> t
- rsplat :: (Build t, GivesThese (Fields t) Identity (RSplat rfun rc), UnifyShape rc t, UnifyShape rfun t) => rfun -> rc -> t
- rsplatA :: (Applicative i, Build t, GivesThese (Fields t) i (RSplatA rfun rc), UnifyShape rc t, UnifyShape rfun t) => rfun -> rc -> i t
- type family UnifyFieldTypes (ss :: [Symbol]) (t :: *) (h :: *) :: Constraint where ...
- type UnifyShape l r = (Shape l r, Shape r l)
- asFieldTypesOf :: UnifyFieldTypes (FieldNames t) t rc => t -> proxy rc -> t
- asShapeOf :: UnifyShape l r => l -> r -> l
- hoid :: forall t a. Hoid t a => a -> a
- hoidProxy :: forall t a. Hoid t a => Proxy a
- data Label s
- type Labels fs = Labels_ '[] -> Labels_ fs
- class NoWarnUnusedTopBind t where
- fieldLabelsOf :: forall t proxy. proxy t -> Labels (FieldNames t)
- makeRecords :: [Name] -> Q [Dec]
- mkLabel :: forall s. Label s
- mkLabels :: forall fs. Labels fs
- proxyOf :: a -> Proxy a
Singleton records
A record type with a single field.
(~) Symbol s1 s2 => Has s1 ((:@) s2 a) Source # | |
Functor ((:@) s) Source # | |
Generic1 ((:@) s) Source # | |
Eq a => Eq ((:@) s a) Source # | |
(Data a, KnownSymbol s) => Data ((:@) s a) Source # | |
Ord a => Ord ((:@) s a) Source # | |
(KnownSymbol s, Show a) => Show ((:@) s a) Source # | |
Generic ((:@) s a) Source # | |
(Lift a, KnownSymbol s) => Lift ((:@) s a) Source # | |
Binary a => Binary ((:@) s a) Source # | |
Serialize a => Serialize ((:@) s a) Source # | |
NFData a => NFData ((:@) s a) Source # | |
Build ((:@) s a) Source # | |
type FieldType s1 ((:@) s2 a) Source # | |
type Rep1 ((:@) s) Source # | |
type Rep ((:@) s a) Source # | |
type Fields ((:@) s a) Source # | |
type Shape ((:@) s _) o Source # | |
Accessing parts of records
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
neither retains extricate1
t
of Done
x -> xt
nor forces x
.
- Strictness
- Forcing the
Done
layer ofextricate1
forces enough oft
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) = ()
type FieldType s t :: * Source #
Default: GenericFieldType
extricate1 :: Label s -> t -> Eval (FieldType s t) Source #
Default: genericExtricate1
extricate1 :: (Generic t, GBox (IsABox (Rep t)) t, GenericHas (Rep t) s (FieldType s t)) => Label s -> t -> Eval (FieldType s t) Source #
Default: genericExtricate1
TypeError Constraint (NoSuchField s ()) => Has s () Source # | This is a non-instance. |
Has s a => Has s (Tup1 a) Source # | |
ClosedHas s (R fd) => Has s (R fd) Source # | |
(DisjointFields a b, Has s (Pair a b)) => Has s (a, b) Source # | |
(~) Symbol s1 s2 => Has s1 ((:@) s2 a) Source # | |
TypeError Constraint (NoFun (Symbol -> * -> Constraint) Has) => Has s (a -> b) Source # | This is a non-instance. |
MightHave (Pair l r) (Find (Pair l r) s) (Pair l r) s (FieldType s (Pair l r)) => Has s (Pair l r) Source # | |
Has_Hide (Elem Symbol s sHiddens) s rc => Has s (Hide sHiddens rc) Source # | |
Has s ((a, b), c) => Has s (a, b, c) Source # | |
Has s ((a, b), (c, d)) => Has s (a, b, c, d) Source # | |
Has s ((a, b, c), (d, e)) => Has s (a, b, c, d, e) Source # | |
Has s ((a, b, c), (d, e, f)) => Has 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 # | |
Has s ((a, b, c, d), (e, f, g, h)) => Has 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 = returnextricate
(#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) -> BoolA
!
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 aNote 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 #
- it only works for expressions,
- it only works inside an
Applicative
.
Hiding fields
Deny the
instance for each of Has
ss
.
Record types' width subtyping
Lowest-level combinators
Record types: product types where each factor has a static name
(i.e. the Fields
).
type Fields (t :: *) :: [(Symbol, *)] Source #
Each element of this list is the name of a field and its
type in t
. Default: GenericFields
.
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
constraint requires --- via Shape
t o~
--- 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.
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
tThis law reasonably requires that
t
Has
all of its ownFields
. - 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 asgenericBuild
. We do not provide aDefaultSignature
because it is most often critical for performance thatbuild
is inlined, which requires an explicitINLINE
pragma (the RHS size gets too large for inferred inlining with even just three fields). We thus recommend the following.instance
Build
Foo where {-# INLINEbuild
#-}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)
Build () Source # | |
Build a => Build (Tup1 a) Source # | |
(KnownFD fd, Hoid FD ([(Symbol, *)] -> FD) MkFD fd) => Build (R fd) Source # | |
TypeError Constraint (NoFun (* -> Constraint) Build) => Build (a -> b) Source # | This is a non-instance. |
(DisjointFields a b, Lemma_AppendGivesThese (Fields a), Build a, Build b) => Build (a, b) Source # | |
Build ((:@) s a) Source # | |
TypeError Constraint NoBuildPair => Build (Pair l r) Source # | This is a non-instance. |
Build ((a, b), c) => Build (a, b, c) Source # | |
Build ((a, b), (c, d)) => Build (a, b, c, d) Source # | |
Build ((a, b, c), (d, e)) => Build (a, b, c, d, e) Source # | |
Build ((a, b, c), (d, e, f)) => Build (a, b, c, d, e, f) Source # | |
Build ((a, b, c, d), (e, f, g)) => Build (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 # | |
Pure combinators
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
.
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.
rup :: forall t rc. (Build t, rc `IsSubtypeOf` t) => rc -> t Source #
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 #
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.
*> :trsplat
[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 wherefpure
_ x = do print x (,) x <$> System.CPUTime.getCPUTime *>rmapA
MkPrintAndTime (dub
#x "OK",dub
#y ()) "OK" () (dub
#x ("OK",43062500000000),dub
#y ((),43062500000000))
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 #
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)
rsplat :: (Build t, GivesThese (Fields t) Identity (RSplat rfun rc), UnifyShape rc t, UnifyShape rfun t) => rfun -> rc -> t infixl 4 Source #
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 #
Ascription
type family UnifyFieldTypes (ss :: [Symbol]) (t :: *) (h :: *) :: Constraint where ... Source #
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.
asserts that hoid
@ta
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.
Conveniences
Use -XOverloadedLabels
to create labels. For example, #x ::
Label "x"
.
Or use mkLabel
.
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 ::
, for example.Labels
'["x","y","z"]
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} instanceNoWarnUnusedTopBind
Bar wherenoWarnUnusedTopBind
MkBar{x=_,y=_} = () instanceHas
"x" Bar instanceHas
"y" Bar instanceBuild
Bar where {-# INLINErupEval
#-}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.
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.