{- | Description: -- This is usually the only import.

#TH#

[<#TH 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 <#conversion the automatic conversions> work.

#singletons#

[<#singletons 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#

[<#projection Projection>]

    The 'Has' class provides the 'extricate' projection, which allows
    <#careful-strictness careful control of strictness>.

    @
      *> :t 'Data.Ruin.Eval.runEval' . 'extricate' \#x
      'Has' "x" t => t -> 'FieldType' "x" t
    @

    'extricate' can navigate nested records with intuitive syntax.

    @
      *> :t 'Data.Ruin.Eval.runEval' . 'extricate' (\#x . \#y)
      ('Has' "y" ('FieldType' "x" t), 'Has' "x" t) =>
       t -> 'FieldType' "y" ('FieldType' "x" t)
    @

#conversion#

[<#conversion 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#

[<#ascription 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
    @

#to-fro#

[<#to-fro 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}
    @

#qq#

[<#qq 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
    @

#suppression#

[<#suppression 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
          * ...
      *> 'Data.Ruin.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#

[<#partitioning 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#

[<#custom-errors 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#

[<#fieldwise 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
    <#fieldwise-more this section> for more information.

#applicative-variants#

[<#applicative-variants 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'.
-}
module Data.Ruin (
  -- * Singleton records
  (:@),
  dub,
  undub,

  -- * Accessing parts of records
  Has(..),
  extricate,
  rna,
  rnaA,

  -- * Hiding fields
  Hide,
  hide,

  -- * Record types' /width subtyping/

  -- ** Lowest-level combinators
  Build(..),

  -- ** Pure combinators
  (<@),
  rdrop,
  rfrom,
  rsym,
  rtake,
  rto,
  rup,

  -- ** Applicative combinators
  rfromA,
  rsymA,
  rtoA,
  rupA,

  -- * Fieldwise combinators
  --
  -- | #fieldwise-more#
  --
  -- 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))
  -- @
  FPure(..),
  rlabel,
  rmempty,
  rmap,
  rmapA,
  rmappend,
  rmonopure,
  rpolypure,
  rpure,
  rsappend,
  rsplat,
  rsplatA,

  -- * Ascription
  UnifyFieldTypes,
  UnifyShape,
  asFieldTypesOf,
  asShapeOf,
  hoid,
  hoidProxy,

  -- * Conveniences
  Label,
  Labels,
  NoWarnUnusedTopBind(..),
  fieldLabelsOf,
  makeRecords,
  mkLabel,
  mkLabels,
  proxyOf,
  ) where

import Data.Ruin.All
import Data.Ruin.Deep (Labels,extricate)
import Data.Ruin.Fieldwise
import Data.Ruin.Hide
import Data.Ruin.Hoid (hoid,hoidProxy)
import Data.Ruin.Internal
import Data.Ruin.QQ (rna,rnaA)
import Data.Ruin.TH (makeRecords)