packages feed

red-black-record 2.1.0.1 → 2.1.0.2

raw patch · 6 files changed

+321/−204 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

README.md view
@@ -1,194 +1,194 @@-# red-black-record
-
-## What's this?
-
-A library that provides extensible records and variants, both indexed by a
-type-level [red-black](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree)
-tree that maps `Symbol` keys to value types of any kind. The keys correspond to
-field names in records, and to branch names in variants. Many record functions
-have their variant mirror-images and viceversa.
-
-At the term level, value types come wrapped in a type constructor of kind `q ->
-Type`, where `q` is the kind of value types. Typically, the type constructor
-will be an [identity
-functor](http://hackage.haskell.org/package/sop-core-0.4.0.0/docs/Data-SOP.html#t:I),
-but it can also be `Maybe` or some other `Applicative` for parsing, validation
-and so on.
-
-If we forget about the keys and only keep the values, records are isomorphic to
-[n-ary unlabeled
-products](http://hackage.haskell.org/package/sop-core-0.4.0.0/docs/Data-SOP.html#t:NP),
-and variants are isomorphic to [n-ary unlabeled
-sums](http://hackage.haskell.org/package/sop-core-0.4.0.0/docs/Data-SOP.html#t:NS).
-The [sop-core](http://hackage.haskell.org/package/sop-core) library provides
-such unlabeled types, along with a rich API for manipulating them. Instead of
-reinventing the wheel, red-black-record defines conversion functions to
-facilitate working in the "unlabeled" world and then coming back to records and
-variants.
-
-There is another world towards which bridges must be built: the everyday
-Haskell world of conventional records and sums. In fact, one of the motivations
-of extensible records and variants is to serve as "generalized" versions of
-vanilla data types. Advanced use cases can rely on these generalized versions,
-thereby avoiding intrusive changes to the original types. red-black-record
-provides conversion typeclasses with default implementations by way of
-[`GHC.Generic`](http://hackage.haskell.org/package/base-4.12.0.0/docs/GHC-Generics.html).
-
-For examples on how to use the library, check the haddocks for the
-`Data.RBR.Examples` module.
-
-## FAQ
-
-### What extensions do I need to use this library?
-
-* `DataKinds`.
-
-* `TypeApplications` to be able to specify field and branch names.
-
-* `TypeFamilies`.
-
-* `FlexibleContexts`.
-
-* `DeriveGeneric` for interfacing with normal records.
-
-* `PartialTypeSignatures` for hiding complex tree types.
-
-### My type signatures are getting big and scary because of those type-level trees. What to do?
-
-The
-[`-XPartialTypeSignatures`](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html?#extension-PartialTypeSignatures)
-extension can help with that, in combination with the
-[-Wno-partial-type-signatures](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wpartial-type-signatures)
-GHC flag that disables the warning message emitted when the underscore is
-encountered in a signature.
-
-The flag can be set globally in the
-[ghc-options](https://www.haskell.org/cabal/users-guide/developing-packages.html?#pkg-field-ghc-options)
-section of the .cabal file, and also for particular modules with the
-[OPTIONS_GHC](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html?highlight=options_ghc#options-ghc-pragma)
-file-header pragma.
-
-### The `Show` instance for record doesn't show any field names.
-
-The field names exist only at the type level. Also, the `Show` instance uses
-n-ary products and sums from
-[sop-core](http://hackage.haskell.org/package/sop-core), which do not have
-field labels.
-
-For fancier output, use the "pretty-show" functions instead.
-
-### Working with two records, I'm getting errors about incompatible types even as both records have the exact same fields.
-
-Alas, the order of insertion in the type-level tree matters :( Different
-insertion orders can produce structurally different trees, even as they encode
-the same symbol-to-type map.
-
-As a workaround, one can use the `-Subset` functions to convert between
-equivalent structures.
-
-### I can't insert into a record when a field with the same name but different type already exists. Why not simply overwrite it?
-
-That limitation was intentional, because allowing it would make impossible to
-implement of `widen` for `Variant`. One solution is to explicitly delete the
-field and then insert it again.
-
-### The library doesn't use Proxy and relies on type application instead. But what’s the order of the type parameters?
-
-For standalone functions, it’s the order in which the type variables appear in
-the `forall`.
-
-### What's the deal with all those -I suffixed versions of functions?
-
-This library aims to provide
-[HKD](http://reasonablypolymorphic.com/blog/higher-kinded-data/)-like
-functionality by wrapping all the fields of a record in a type constructor.
-
-But sometimes we are working with "pure" records without effects, and we just
-want to get and set a field's value. In that case, the type constructor that
-wraps each field will be an identity functor `I` (from
-[sop-core](http://hackage.haskell.org/package/sop-core)). The -I suffixed
-functions wrap and unwrap the field's value on behalf of the user.
-
-### What's the deal with all those -Subset suffixed versions of functions?
-
-These functions target multiple fields or branches at the same time. They can
-be used to build lawful lenses and prisms over fragmenst of a structure.
-
-They can also be used to convert between type-level trees that have the same
-entries but different structure.
-
-### What about compilation times?
-
-Compilation times balloon for large records. In the tests folder there's
-an example (not run by default in the tests) of the construction of a 50-field
-record whose fields are afterwards accessed one by one. It takes about 22
-seconds to compile in my machine. 
-
-Code involving deletion of fields and branches (like using the `winnow`
-function for `Variant`s) is currently poorly optimized and will compile
-[slower](https://github.com/danidiaz/red-black-record/issues/12) than that.
-
-The default generics-based implementations of `FromRecord` and `FromVariant`
-use the same type-level machinery as the getters and its use will likely slow
-down compilation as well.
-
-## Inspirations
-
-* The code for the red-black tree has been lifted from Stefan Kahrs's code
-  [available
-  here](https://www.cs.kent.ac.uk/people/staff/smk/redblack/rb.html). See also
-  [this post](https://www.cs.kent.ac.uk/people/staff/smk/redblack/rb.html).
-
-* Besides depending on sop-core, I have copied and adapted code from it. In
-  particular the `KeysValuessAll` typeclass is a version of the `All` typeclass
-  from sop-core. 
-
-* [Surgery for data
-  types](https://blog.poisson.chat/posts/2018-11-26-type-surgery.html).
-  [reddit](https://www.reddit.com/r/haskell/comments/a0gi4z/surgery_for_data_types/).
-
-## Alternatives
-
-* [generics-sop](http://hackage.haskell.org/package/generics-sop) and
-  [records-sop](http://hackage.haskell.org/package/records-sop). Like
-  red-black-record, both of these libraries build upon sop-core. They are in
-  fact written by the same author of sop-core. generics-sop can provide
-  sum-of-products representations of any datatype with a Generic instance
-  (red-black-record is more limited, it only converts types that fit the named
-  record or variant mold—so no types with anonymous fields for example). 
-  
-  If you don't need to explicitly target *individual* fields in the generic
-  representation, you'll be better off using generics-sop instead of
-  red-black-record. 
-  
-  On top of generics-sop, records-sop provides named field accessors and record
-  subtyping based on a type-level list of fields (unlike the type-level tree
-  used by red-black-record). It doesn't seem to provide variants.
-
-* [superrecord](http://hackage.haskell.org/package/superrecord). This library
-  provides very efficient field access at runtime because the fields are backed
-  internally by an array. Uses a *sorted* type-level list of fields, to avoid
-  the problems of multiple orderings of the same fields.
-
-* [vinyl](http://hackage.haskell.org/package/vinyl). One of the oldest and more
-  fully-featured extensible records libraries. Uses a type level list of
-  fields. The fields' values are wrapped in a type constructor, like in
-  sop-core. The records seem to use an auxiliary sum type that serves as a
-  "code" for the fields. See also
-  [vinyl-genercics](https://hackage.haskell.org/package/vinyl-generics).
-
-* [HTree](https://github.com/i-am-tom/learn-me-a-haskell#htree). Another
-  implementation of extensible records using type-level red-black trees.
-
-* [megarecord](https://github.com/jvanbruegge/Megarecord). Seems to be a
-  proof-of-concept for a future [row polymorphism
-  extension](https://github.com/ghc-proposals/ghc-proposals/pull/180) for
-  Haskell.
-
-* [generic-data-surgery](https://hackage.haskell.org/package/generic-data-surgery).
-  Lots of useful machinery for manipulating generic representations of
-  dataytpes, without requiring intrusive changes to the original
-  representation.
-
-* [Coxswain](https://ghc.haskell.org/trac/ghc/wiki/Plugins/TypeChecker/RowTypes/Coxswain).
-
+# red-black-record++## What's this?++A library that provides extensible records and variants, both indexed by a+type-level [red-black](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree)+tree that maps `Symbol` keys to value types of any kind. The keys correspond to+field names in records, and to branch names in variants. Many record functions+have their variant mirror-images and viceversa.++At the term level, value types come wrapped in a type constructor of kind `q ->+Type`, where `q` is the kind of value types. Typically, the type constructor+will be an [identity+functor](http://hackage.haskell.org/package/sop-core-0.4.0.0/docs/Data-SOP.html#t:I),+but it can also be `Maybe` or some other `Applicative` for parsing, validation+and so on.++If we forget about the keys and only keep the values, records are isomorphic to+[n-ary unlabeled+products](http://hackage.haskell.org/package/sop-core-0.4.0.0/docs/Data-SOP.html#t:NP),+and variants are isomorphic to [n-ary unlabeled+sums](http://hackage.haskell.org/package/sop-core-0.4.0.0/docs/Data-SOP.html#t:NS).+The [sop-core](http://hackage.haskell.org/package/sop-core) library provides+such unlabeled types, along with a rich API for manipulating them. Instead of+reinventing the wheel, red-black-record defines conversion functions to+facilitate working in the "unlabeled" world and then coming back to records and+variants.++There is another world towards which bridges must be built: the everyday+Haskell world of conventional records and sums. In fact, one of the motivations+of extensible records and variants is to serve as "generalized" versions of+vanilla data types. Advanced use cases can rely on these generalized versions,+thereby avoiding intrusive changes to the original types. red-black-record+provides conversion typeclasses with default implementations by way of+[`GHC.Generic`](http://hackage.haskell.org/package/base-4.12.0.0/docs/GHC-Generics.html).++For examples on how to use the library, check the haddocks for the+`Data.RBR.Examples` module.++## FAQ++### What extensions do I need to use this library?++* `DataKinds`.++* `TypeApplications` to be able to specify field and branch names.++* `TypeFamilies`.++* `FlexibleContexts`.++* `DeriveGeneric` for interfacing with normal records.++* `PartialTypeSignatures` for hiding complex tree types.++### My type signatures are getting big and scary because of those type-level trees. What to do?++The+[`-XPartialTypeSignatures`](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html?#extension-PartialTypeSignatures)+extension can help with that, in combination with the+[-Wno-partial-type-signatures](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wpartial-type-signatures)+GHC flag that disables the warning message emitted when the underscore is+encountered in a signature.++The flag can be set globally in the+[ghc-options](https://www.haskell.org/cabal/users-guide/developing-packages.html?#pkg-field-ghc-options)+section of the .cabal file, and also for particular modules with the+[OPTIONS_GHC](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html?highlight=options_ghc#options-ghc-pragma)+file-header pragma.++### The `Show` instance for record doesn't show any field names.++The field names exist only at the type level. Also, the `Show` instance uses+n-ary products and sums from+[sop-core](http://hackage.haskell.org/package/sop-core), which do not have+field labels.++For fancier output, use the "pretty-show" functions instead.++### Working with two records, I'm getting errors about incompatible types even as both records have the exact same fields.++Alas, the order of insertion in the type-level tree matters :( Different+insertion orders can produce structurally different trees, even as they encode+the same symbol-to-type map.++As a workaround, one can use the `-Subset` functions to convert between+equivalent structures.++### I can't insert into a record when a field with the same name but different type already exists. Why not simply overwrite it?++That limitation was intentional, because allowing it would make impossible to+implement of `widen` for `Variant`. One solution is to explicitly delete the+field and then insert it again.++### The library doesn't use Proxy and relies on type application instead. But what’s the order of the type parameters?++For standalone functions, it’s the order in which the type variables appear in+the `forall`.++### What's the deal with all those -I suffixed versions of functions?++This library aims to provide+[HKD](http://reasonablypolymorphic.com/blog/higher-kinded-data/)-like+functionality by wrapping all the fields of a record in a type constructor.++But sometimes we are working with "pure" records without effects, and we just+want to get and set a field's value. In that case, the type constructor that+wraps each field will be an identity functor `I` (from+[sop-core](http://hackage.haskell.org/package/sop-core)). The -I suffixed+functions wrap and unwrap the field's value on behalf of the user.++### What's the deal with all those -Subset suffixed versions of functions?++These functions target multiple fields or branches at the same time. They can+be used to build lawful lenses and prisms over fragmenst of a structure.++They can also be used to convert between type-level trees that have the same+entries but different structure.++### What about compilation times?++Compilation times balloon for large records. In the tests folder there's+an example (not run by default in the tests) of the construction of a 50-field+record whose fields are afterwards accessed one by one. It takes about 22+seconds to compile in my machine. ++Code involving deletion of fields and branches (like using the `winnow`+function for `Variant`s) is currently poorly optimized and will compile+[slower](https://github.com/danidiaz/red-black-record/issues/12) than that.++The default generics-based implementations of `FromRecord` and `FromVariant`+use the same type-level machinery as the getters and its use will likely slow+down compilation as well.++## Inspirations++* The code for the red-black tree has been lifted from Stefan Kahrs's code+  [available+  here](https://www.cs.kent.ac.uk/people/staff/smk/redblack/rb.html). See also+  [this post](https://www.cs.kent.ac.uk/people/staff/smk/redblack/rb.html).++* Besides depending on sop-core, I have copied and adapted code from it. In+  particular the `KeysValuessAll` typeclass is a version of the `All` typeclass+  from sop-core. ++* [Surgery for data+  types](https://blog.poisson.chat/posts/2018-11-26-type-surgery.html).+  [reddit](https://www.reddit.com/r/haskell/comments/a0gi4z/surgery_for_data_types/).++## Alternatives++* [generics-sop](http://hackage.haskell.org/package/generics-sop) and+  [records-sop](http://hackage.haskell.org/package/records-sop). Like+  red-black-record, both of these libraries build upon sop-core. They are in+  fact written by the same author of sop-core. generics-sop can provide+  sum-of-products representations of any datatype with a Generic instance+  (red-black-record is more limited, it only converts types that fit the named+  record or variant mold—so no types with anonymous fields for example). +  +  If you don't need to explicitly target *individual* fields in the generic+  representation, you'll be better off using generics-sop instead of+  red-black-record. +  +  On top of generics-sop, records-sop provides named field accessors and record+  subtyping based on a type-level list of fields (unlike the type-level tree+  used by red-black-record). It doesn't seem to provide variants.++* [superrecord](http://hackage.haskell.org/package/superrecord). This library+  provides very efficient field access at runtime because the fields are backed+  internally by an array. Uses a *sorted* type-level list of fields, to avoid+  the problems of multiple orderings of the same fields.++* [vinyl](http://hackage.haskell.org/package/vinyl). One of the oldest and more+  fully-featured extensible records libraries. Uses a type level list of+  fields. The fields' values are wrapped in a type constructor, like in+  sop-core. The records seem to use an auxiliary sum type that serves as a+  "code" for the fields. See also+  [vinyl-genercics](https://hackage.haskell.org/package/vinyl-generics).++* [HTree](https://github.com/i-am-tom/learn-me-a-haskell#htree). Another+  implementation of extensible records using type-level red-black trees.++* [megarecord](https://github.com/jvanbruegge/Megarecord). Seems to be a+  proof-of-concept for a future [row polymorphism+  extension](https://github.com/ghc-proposals/ghc-proposals/pull/180) for+  Haskell.++* [generic-data-surgery](https://hackage.haskell.org/package/generic-data-surgery).+  Lots of useful machinery for manipulating generic representations of+  dataytpes, without requiring intrusive changes to the original+  representation.++* [Coxswain](https://ghc.haskell.org/trac/ghc/wiki/Plugins/TypeChecker/RowTypes/Coxswain).+
lib/Data/RBR.hs view
@@ -1,5 +1,25 @@+{-| 
+    This module provides extensible 'Record' and 'Variant' types, which are
+    indexed by a type-level 'Map'.
+
+    Many functions in this module require the use of @TypeApplications@ to
+    avoid ambiguity. The order of the applications is the order of the type
+    variables in the function signature's @forall@. The first type variable is
+    usually the field/branch name and it's always required. The other type
+    variables can often be inferred.
+
+    Meaning of commonly used type and kind variables:
+
+        - @t@: A type-level 'Map', usually of kind @Map Symbol q@.
+        - @k@: A key of kind 'Symbol' in a type-level 'Map'. 
+        - @v@: A type value of kind @q@ in a type-level 'Map'.
+        - @q@: The kind of the type value @v@.
+        - @f@: A type constructor of kind @q -> Type@ that wraps the type @v@. 
+        - @flat@: A type-level list of kind @[q]@ whose elements correspond to values in a type-level 'Map'.
+        
+-}
 module Data.RBR (
-        -- * Type-level Red-Black tree
+        -- * Type-level map
         -- $typelevel
        Map,
        Empty,
@@ -80,13 +100,13 @@        Productlike (..),
        prefixNP,
        breakNP,
-       fromNP,
        toNP,
+       fromNP,
        Sumlike (..),
        prefixNS,
        breakNS,
-       fromNS,
        toNS,
+       fromNS,
        -- * Data.SOP re-exports
        I(..),
        K(..),
@@ -109,10 +129,10 @@ 
 {- $typelevel
  
-   A Red-Black tree that is used at the type level, with @DataKinds@. The tree
-   keeps track of what keys are present and to what types they correspond.
- 
+   A type-level map that keeps track of which keys are present, and to which
+   types they correspond.
 
+   Implemented as a red-black tree, and used as a kind by means of @DataKinds@. 
 -} 
 
 {- $nominal
lib/Data/RBR/Examples.hs view
@@ -31,6 +31,9 @@     
     -- * Ensuring all branches of a sum type are parsed from JSON
     -- $json4sum
+
+    -- * External examples
+    -- $externalexamples
     ) where
 
 import Data.RBR
@@ -331,6 +334,15 @@      in s
     :}
 That 70
+
+-}
+
+{- $externalexamples
+ 
+    * [Is there a canonical way of comparing/changing one/two records in haskell? (SO)](https://stackoverflow.com/a/57574731/1364288)
+    * [Help with Generics. (Reddit)](https://www.reddit.com/r/haskell/comments/cteemj/help_with_generics/expyjfk)
+    * [Adventures assembling records of capabilities. (Discourse)](https://discourse.haskell.org/t/adventures-assembling-records-of-capabilities/623)
+    * [Resources on sop-core and generics-sop. (GitHub)](https://github.com/well-typed/generics-sop/issues/47)
 
 -}
 
lib/Data/RBR/Internal.hs view
@@ -45,6 +45,18 @@ import           Data.SOP.NP (collapse_NP,liftA_NP,liftA2_NP,cliftA_NP,cliftA2_NP,pure_NP)
 import           Data.SOP.NS (collapse_NS,ap_NS,injections,Injection)
 
+
+{- $setup
+ 
+>>> :set -XDataKinds -XTypeApplications -XPartialTypeSignatures -XFlexibleContexts -XTypeFamilies -XDeriveGeneric 
+>>> :set -Wno-partial-type-signatures  
+>>> import Data.RBR
+>>> import Data.SOP
+>>> import GHC.Generics
+
+-}
+
+
 -- | The color of a node.
 data Color = R
            | B
@@ -111,6 +123,10 @@      The names are represented by a constant functor 'K' carrying an annotation
      of type 'String'. This means that there aren't actually any values of the
      type that corresponds to each field, only the 'String' annotations.
+
+>>> putStrLn $ prettyShowRecord show $ demoteKeys @(Insert "foo" Char (Insert "bar" Bool Empty))
+{bar = K "bar", foo = K "foo"}
+
 -} 
 demoteKeys :: forall t. KeysValuesAll KnownKey t => Record (K String) t
 demoteKeys = cpara_Map (Proxy @KnownKey) unit go
@@ -133,6 +149,9 @@   Create a record containing the names of each field along with a term-level
   representation of each type.
 
+>>> putStrLn $ prettyShowRecord show $ demoteEntries @(Insert "foo" Char (Insert "bar" Bool Empty))
+{bar = K ("bar",Bool), foo = K ("foo",Char)}
+
   See also 'collapse_Record' for getting the entries as a list.
 -}
 demoteEntries :: forall t. KeysValuesAll KnownKeyTypeableValue t => Record (K (String,TypeRep)) t
@@ -175,6 +194,12 @@ 
 {- | Collapse a 'Record' composed of 'K' annotations.
     
+>>> collapse_Record unit
+[]
+
+>>> collapse_Record (insert @"bar" (K False) unit)
+[False]
+
      The naming scheme follows that of 'Data.SOP.NP.collapse_NP'.
 
 -}
@@ -265,6 +290,13 @@ 
 {- |
      Adds a new field to a 'Record'.
+
+>>> project @"foo" (insert @"foo" (I 'a') unit)
+I 'a'
+
+>>> project @"foo" (insert @"foo" @Char Nothing unit)
+Nothing
+
  -}
 insert :: forall k v t f. Insertable k v t => f v -> Record f t -> Record f (Insert k v t)
 insert = _insert @_ @k @v @t @f
@@ -282,6 +314,10 @@ addField = insert @k @v @t @f
 
 {- | Like 'insert' but specialized to pure 'Record's.
+ 
+>>> projectI @"foo" (insertI @"foo" 'a' unit)
+'a'
+
 -}
 insertI :: forall k v t . Insertable k v t => v -> Record I t -> Record I (Insert k v t)
 insertI = insert @k @v @t . I
@@ -652,6 +688,8 @@                Here)
 
 {- | Get the value of a field for a 'Record'. 
+
+
 -}
 project :: forall k t f . Key k t => Record f t -> f (Value k t)
 project = snd . field @k @t
@@ -672,6 +710,10 @@ modifyField f r = uncurry ($) (fmap f (field @k @t @f r))
 
 {- | Put a value into the branch of a 'Variant'.
+
+>>> match @"foo" (inject @"foo" (I 'a') :: Variant I (Insert "foo" Char Empty))
+Just (I 'a')
+
 -}
 inject :: forall k t f. Key k t => f (Value k t) -> Variant f t
 inject = snd (branch @k @t)
@@ -682,6 +724,10 @@ match = fst (branch @k @t)
 
 {- | Like 'project' but specialized to pure 'Record's.
+
+>>> projectI @"foo" (insertI @"foo" 'a' (insertI @"bar" False unit))
+'a'
+
 -}
 projectI :: forall k t . Key k t => Record I t -> Value k t
 projectI = unI . snd . field @k @t
@@ -702,6 +748,10 @@ modifyFieldI f = modifyField @k @t (I . f . unI)
 
 {- | Like 'inject' but specialized to pure 'Variant's.
+ 
+>>> matchI @"foo" (injectI @"foo" 'a' :: Variant I (Insert "foo" Char Empty))
+Just 'a'
+
 -}
 injectI :: forall k t. Key k t => Value k t -> Variant I t
 injectI = snd (branch @k @t) . I
@@ -713,6 +763,10 @@ 
 {- | Process a 'Variant' using a eliminator 'Record' that carries
      handlers for each possible branch of the 'Variant'.
+
+>>> eliminate (addCaseI @"foo" @Int succ (addCaseI @"bar" pred unit)) (injectI @"bar" 33)
+32
+
 -}
 eliminate :: (Productlike '[] t result, Sumlike '[] t result, SListI result) => Record (Case f r) t -> Variant f t -> r
 eliminate cases variant = 
@@ -754,6 +808,7 @@                        SListI flat)
 
 {- | Like 'field', but targets multiple fields at the same time 
+
 -}
 fieldSubset :: forall subset whole flat f. (ProductlikeSubset subset whole flat) 
             => Record f whole -> (Record f subset -> Record f whole, Record f subset)
@@ -777,7 +832,12 @@       in cpara_Map (Proxy @(PresentIn whole)) unit goget)
 
 {- | Like 'project', but extracts multiple fields at the same time.
+
+     The types in the subset tree can often be inferred and left as wildcards in type signature.
  
+>>> prettyShowRecordI $ projectSubset @(Insert "foo" _ (Insert "bar" _ Empty)) (insertI @"foo" 'a' (insertI @"bar" True (insertI @"baz" (Just ()) unit)))
+"{bar = True, foo = 'a'}"
+
      Can also be used to convert between 'Record's with structurally dissimilar
      type-level maps that nevertheless hold the same entries. 
 -}
@@ -916,12 +976,21 @@ breakNP :: forall start t result f. Productlike start t result => NP f result -> (Record f t, NP f start)
 breakNP = _breakNP @_ @start @t @result
 
-{- | Convert a 'Record' into a n-ary product. 
+{- | Convert a 'Record' into a n-ary product. The order of the elements in the
+     product is not the order of insertion in the record.
+
+>>> toNP (insertI @"foo" 'a' (insertI @"bar" True unit))
+I True :* I 'a' :* Nil 
+
 -}
 toNP :: forall t result f. Productlike '[] t result => Record f t -> NP f result
 toNP r = prefixNP r Nil
 
-{- | Convert a n-ary product into a compatible 'Record'. 
+{- | Convert a n-ary product into a compatible 'Record'. Usually follows an invocation of 'toNP'. 
+
+>>> prettyShowRecordI . fromNP @(Insert "foo" _ (Insert "bar" _ Empty)) . toNP $ insertI @"foo" 'a' (insertI @"bar" True unit)
+"{bar = True, foo = 'a'}"
+
 -}
 fromNP :: forall t result f. Productlike '[] t result => NP f result -> Record f t
 fromNP np = let (r,Nil) = breakNP np in r
@@ -1011,11 +1080,19 @@ breakNS = _breakNS @_ @start @t @result
 
 {- | Convert a 'Variant' into a n-ary sum. 
+ 
+>>> toNS (injectI @"foo" 'a' :: Variant I (Insert "foo" Char (Insert "bar" Bool Empty)))
+S (Z (I 'a')) 
+
 -}
 toNS :: forall t result f. Sumlike '[] t result => Variant f t -> NS f result
 toNS = prefixNS . Right
 
 {- | Convert a n-ary sum into a compatible 'Variant'. 
+ 
+>>> prettyShowVariantI $ fromNS @(Insert "foo" _ (Insert "bar" _ Empty)) . toNS $ (injectI @"foo" 'a' :: Variant I (Insert "foo" Char (Insert "bar" Bool Empty)))
+"foo ('a')"
+
 -}
 fromNS :: forall t result f. Sumlike '[] t result => NS f result -> Variant f t
 fromNS ns = case breakNS ns of 
@@ -1722,6 +1799,13 @@ winnow = _winnow @_ @k @v @t 
 
 {- | Like 'winnow' but specialized to pure 'Variant's.
+ 
+>>> winnow @"bar" @Bool (injectI @"bar" False :: Variant I (Insert "foo" Char (Insert "bar" Bool Empty)))
+Right (I False)
+
+>>> prettyShowVariantI `first` winnow @"foo" @Char (injectI @"bar" False :: Variant I (Insert "foo" Char (Insert "bar" Bool Empty)))
+Left "bar (False)" 
+
 -}
 winnowI :: forall k v t . Deletable k v t => Variant I t -> Either (Variant I (Delete k v t)) v
 winnowI = fmap unI . winnow @k @v @t
red-black-record.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.0
 name:                red-black-record
-version:             2.1.0.1
+version:             2.1.0.2
 synopsis:            Extensible records and variants indexed by a type-level Red-Black tree.
 
 description:         A library that provides extensible records and variants,
@@ -11,7 +11,7 @@                      names in records, and to branch names in variants.
                      . 
                      At the term level, value types come wrapped in a type
-                     constructor of kind `q -> Type`, where `q` is the kind of
+                     constructor of kind @q -> Type@, where @q@ is the kind of
                      value types.
                      .
                      The records and variants can be converted to and from
tests/doctests.hs view
@@ -2,5 +2,6 @@ 
 main = doctest ["-ilib", 
                 "lib/Data/RBR.hs",
+                "lib/Data/RBR/Internal.hs",
                 "lib/Data/RBR/Examples.hs"
                ]