on master help sign in
71 files changed+5190−1095
since splitsnapshots
unifiedsplit
.github/FUNDING.yml+12 .gitignore+4 .travis.yml+152−16 ChangeLog.md+55−3 LICENSE+1−1 README.md+244−68 cabal.haskell-ci+5 cabal.project+2 examples/Biscuits.hs+150 examples/Examples.hs+98−9 examples/StarWars.hs+1−1 examples/doctest.hs+7 generic-lens.cabal+112−17 src/Data/GenericLens/Internal.hs+64 src/Data/GenericLens/Internal.hs~ src/Data/Generics/Internal/Errors.hs+59 src/Data/Generics/Internal/Families.hs+21−3 src/Data/Generics/Internal/Families/Changing.hs+161 src/Data/Generics/Internal/Families/Collect.hs+133 src/Data/Generics/Internal/Families/Count.hs−75 src/Data/Generics/Internal/Families/Has.hs+87−50 src/Data/Generics/Internal/GenericN.hs+75 src/Data/Generics/Internal/Lens.hs−109 src/Data/Generics/Internal/Profunctor/Iso.hs+111 src/Data/Generics/Internal/Profunctor/Lens.hs+171 src/Data/Generics/Internal/Profunctor/Prism.hs+117 src/Data/Generics/Internal/VL/Iso.hs+80 src/Data/Generics/Internal/VL/Lens.hs+70 src/Data/Generics/Internal/VL/Prism.hs+74 src/Data/Generics/Internal/VL/Traversal.hs+92 src/Data/Generics/Internal/Void.hs+19 src/Data/Generics/Labels.hs+142 src/Data/Generics/Product.hs+15−9 src/Data/Generics/Product/Any.hs+28−25 src/Data/Generics/Product/Constraints.hs+57 src/Data/Generics/Product/Fields.hs+193−59 src/Data/Generics/Product/HList.hs+46 src/Data/Generics/Product/Internal/Constraints.hs+136 src/Data/Generics/Product/Internal/Fields.hs−71 src/Data/Generics/Product/Internal/GLens.hs+81 src/Data/Generics/Internal/HList.hs → src/Data/Generics/Product/Internal/HList.hs+154−64 src/Data/Generics/Product/Internal/Positions.hs+47−31 src/Data/Generics/Product/Internal/Subtype.hs+15−10 src/Data/Generics/Product/Internal/Typed.hs−62 src/Data/Generics/Product/Param.hs+121 src/Data/Generics/Product/Positions.hs+165−41 src/Data/Generics/Product/Subtype.hs+106−30 src/Data/Generics/Product/Typed.hs+105−34 src/Data/Generics/Product/Types.hs+367 src/Data/Generics/Sum.hs+5−6 src/Data/Generics/Sum/Any.hs+40−34 src/Data/Generics/Sum/Constructors.hs+145−39 src/Data/Generics/Sum/Internal/Constructors.hs+29−16 src/Data/Generics/Sum/Internal/Subtype.hs+77−36 src/Data/Generics/Sum/Internal/Typed.hs+38−54 src/Data/Generics/Sum/Subtype.hs+96−46 src/Data/Generics/Sum/Typed.hs+110−54 src/Data/Generics/Wrapped.hs+104 stack.yaml+1−4 test/Bifunctor.hs+47 test/CustomChildren.hs+63 test/Spec.hs+234−18 test/Test24.hs+47 test/Test25.hs+33 test/Test40.hs+22 test/Test62.hs+20 test/Test63.hs+12 test/Test71.hs+17 test/Util.hs+11 test/doctest.hs+8 test/syb/Tree.hs+76
.github/FUNDING.ymlnew file+12
@@ -0,0 +1,12 @@
1+# These are supported funding model platforms
2+
3+github: [kcsongor] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4+patreon: # Replace with a single Patreon username
5+open_collective: # Replace with a single Open Collective username
6+ko_fi: # Replace with a single Ko-fi username
7+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9+liberapay: # Replace with a single Liberapay username
10+issuehunt: # Replace with a single IssueHunt username
11+otechie: # Replace with a single Otechie username
12+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
.gitignore+4
@@ -6,3 +6,7 @@ cabal.sandbox.config
66.ghc*
77*.hi
88*.o
9+core-html
10+*~
11+*.org
12+cabal.project.local
.travis.yml+152−16
@@ -1,19 +1,155 @@
1−sudo: false
2−
1+# This Travis job script has been generated by a script via
2+#
3+# haskell-ci '--config=cabal.haskell-ci' 'cabal.project'
4+#
5+# For more information, see https://github.com/haskell-CI/haskell-ci
6+#
7+# version: 0.5.20190908
8+#
39language: c
4−
5−addons:
6− apt:
7− packages:
8− - libgmp-dev
9−
10−before_install:
11−- mkdir -p ~/.local/bin
12−- export PATH=$HOME/.local/bin:$PATH
13−- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
14−
15−script: stack --no-terminal --install-ghc test
16−
10+dist: xenial
11+git:
12+ # whether to recursively clone submodules
13+ submodules: false
1714cache:
1815 directories:
19− - $HOME/.stack
16+ - $HOME/.cabal/packages
17+ - $HOME/.cabal/store
18+before_cache:
19+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
20+ # remove files that are regenerated by 'cabal update'
21+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
22+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
23+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
24+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
25+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
26+ - rm -rfv $CABALHOME/packages/head.hackage
27+matrix:
28+ include:
29+ - compiler: ghc-8.8.1
30+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}}
31+ - compiler: ghc-8.6.5
32+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-3.0"]}}
33+ - compiler: ghc-8.4.4
34+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-3.0"]}}
35+ - compiler: ghc-8.4.1
36+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.1","cabal-install-3.0"]}}
37+ - compiler: ghc-8.2.2
38+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-3.0"]}}
39+ - compiler: ghc-8.0.2
40+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-3.0"]}}
41+before_install:
42+ - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
43+ - WITHCOMPILER="-w $HC"
44+ - HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//')
45+ - HCPKG="$HC-pkg"
46+ - unset CC
47+ - CABAL=/opt/ghc/bin/cabal
48+ - CABALHOME=$HOME/.cabal
49+ - export PATH="$CABALHOME/bin:$PATH"
50+ - TOP=$(pwd)
51+ - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
52+ - echo $HCNUMVER
53+ - CABAL="$CABAL -vnormal+nowrap+markoutput"
54+ - set -o pipefail
55+ - |
56+ echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk
57+ echo 'BEGIN { state = "output"; }' >> .colorful.awk
58+ echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk
59+ echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk
60+ echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk
61+ echo ' if (state == "cabal") {' >> .colorful.awk
62+ echo ' print blue($0)' >> .colorful.awk
63+ echo ' } else {' >> .colorful.awk
64+ echo ' print $0' >> .colorful.awk
65+ echo ' }' >> .colorful.awk
66+ echo '}' >> .colorful.awk
67+ - cat .colorful.awk
68+ - |
69+ color_cabal_output () {
70+ awk -f $TOP/.colorful.awk
71+ }
72+ - echo text | color_cabal_output
73+install:
74+ - ${CABAL} --version
75+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
76+ - TEST=--enable-tests
77+ - if [ $HCNUMVER -ge 80600 ] ; then TEST=--disable-tests ; fi
78+ - BENCH=--enable-benchmarks
79+ - if [ $HCNUMVER -ge 80800 ] ; then BENCH=--disable-benchmarks ; fi
80+ - HEADHACKAGE=false
81+ - rm -f $CABALHOME/config
82+ - |
83+ echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
84+ echo "remote-build-reporting: anonymous" >> $CABALHOME/config
85+ echo "write-ghc-environment-files: always" >> $CABALHOME/config
86+ echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
87+ echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
88+ echo "world-file: $CABALHOME/world" >> $CABALHOME/config
89+ echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
90+ echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
91+ echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
92+ echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
93+ echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
94+ echo "install-dirs user" >> $CABALHOME/config
95+ echo " prefix: $CABALHOME" >> $CABALHOME/config
96+ echo "repository hackage.haskell.org" >> $CABALHOME/config
97+ echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
98+ - |
99+ echo "program-default-options" >> $CABALHOME/config
100+ echo " ghc-options: $GHCJOBS +RTS -M6G -RTS" >> $CABALHOME/config
101+ - cat $CABALHOME/config
102+ - rm -fv cabal.project cabal.project.local cabal.project.freeze
103+ - travis_retry ${CABAL} v2-update -v
104+ # Generate cabal.project
105+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
106+ - touch cabal.project
107+ - |
108+ echo "packages: ." >> cabal.project
109+ - |
110+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(generic-lens)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
111+ - cat cabal.project || true
112+ - cat cabal.project.local || true
113+ - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
114+ - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
115+ - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
116+ - rm cabal.project.freeze
117+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all | color_cabal_output
118+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all | color_cabal_output
119+script:
120+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
121+ # Packaging...
122+ - ${CABAL} v2-sdist all | color_cabal_output
123+ # Unpacking...
124+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
125+ - cd ${DISTDIR} || false
126+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
127+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
128+ - PKGDIR_generic_lens="$(find . -maxdepth 1 -type d -regex '.*/generic-lens-[0-9.]*')"
129+ # Generate cabal.project
130+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
131+ - touch cabal.project
132+ - |
133+ echo "packages: ${PKGDIR_generic_lens}" >> cabal.project
134+ - |
135+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(generic-lens)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
136+ - cat cabal.project || true
137+ - cat cabal.project.local || true
138+ # Building...
139+ # this builds all libraries and executables (without tests/benchmarks)
140+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
141+ # Building with tests and benchmarks...
142+ # build & run tests, build benchmarks
143+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
144+ # Testing...
145+ - if [ $HCNUMVER -lt 80600 ] ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
146+ # cabal check...
147+ - (cd ${PKGDIR_generic_lens} && ${CABAL} -vnormal check)
148+ # haddock...
149+ - ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all | color_cabal_output
150+ # Building without installed constraints for packages in global-db...
151+ - rm -f cabal.project.local
152+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
153+
154+# REGENDATA ["--config=cabal.haskell-ci","cabal.project"]
155+# EOF
ChangeLog.md+55−3
@@ -1,5 +1,57 @@
1−# Revision history for generic-lens
1+## 1.2.0.1
2+- Give HasAny/AsAny the same VTA behavior on 8.6 and 8.8 (Ryan Scott)
23
3−## 0.1.0.0 -- YYYY-mm-dd
4+## 1.2.0.0
5+- Add `HasTypesUsing` and `HasTypesCustom` for custom traversals (Lysxia)
6+- Improve type errors when no Generic instance is defined
7+- `types` now supports Text by default
48
5−* First version. Released on an unsuspecting world.
9+### Breaking API changes
10+- `HasType` now includes a reflexive case so that every type 'contains' itself (Matt Parsons)
11+- `AsSubtype` and `Subtype` now include a reflexive case so that every type is a subtype of itself
12+
13+## 1.1.0.0
14+- Fix regression in type inference for polymorphic optics
15+- Add `HasField0`, `HasPosition0`, `AsConstructor0`, `HasField_`, `HasPositon_`, and `AsConstructor_` (Lysxia)
16+- `types` now supports Data.Word and Data.Int (Lysxia)
17+- Add `Wrapped` iso for newtypes (Isaac Elliott)
18+- Expose internals through Data.GenericLens.Internal
19+- Add labels for prisms (Daniel Winograd-Cort)
20+
21+## 1.0.0.2
22+- Fix compile-time performance regression
23+
24+## 1.0.0.1
25+- Remove dump-core dependency
26+- Relax upper bound on criterion (#42)
27+
28+## 1.0.0.0
29+- Traversals (types, param, constraints)
30+- Prisms are now optimal too
31+- Monomorphic versions of lenses and prisms also included
32+
33+### Breaking API changes
34+- `projectSub` now returns `Maybe sub` instead of `Either sup sub` (#21)
35+
36+## 0.5.1.0
37+- Infer input type from result type (#25)
38+- Allow changing of multiple type parameters (#24)
39+- Allow changing of type parameters that have kinds other than `*` (#23)
40+- Fix error message in subtype lens
41+
42+## 0.5.0.0
43+
44+- Lenses and prisms are now type-changing.
45+- More informative error messages
46+- More readable type signatures in type errors and when using `:t`
47+- Use `doctest`
48+- Include examples in Haddock
49+
50+### Breaking API changes
51+
52+- The type parameters of the classes have been changed to accommodate
53+ the type-changing update:
54+
55+ `class HasField name a s` -> `class HasField name s t a b` etc.
56+
57+ Accordingly, `field :: Lens' s a` -> `field :: Lens s t a b`
LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c) 2017, Csongor Kiss
1+Copyright (c) 2019, Csongor Kiss
22
33All rights reserved.
44
README.md+244−68
@@ -1,29 +1,71 @@
11# generic-lens
22
3−[![Build
4−Status](https://travis-ci.org/kcsongor/generic-lens.svg?branch=master)](https://travis-ci.org/kcsongor/generic-lens)
3+[![Build Status](https://travis-ci.org/kcsongor/generic-lens.svg?branch=master)](https://travis-ci.org/kcsongor/generic-lens)
54[![Hackage](https://img.shields.io/hackage/v/generic-lens.svg)](https://hackage.haskell.org/package/generic-lens)
65
7−Generically derive lenses and prisms for data types.
6+Generically derive traversals, lenses and prisms.
87
98Available on [Hackage](https://hackage.haskell.org/package/generic-lens)
109
11−This package uses the GHC 8 `Generic` representation to derive various operations
12−on data structures with lens interfaces, including structural subtype
13−relationships between records and positional indexing into arbitrary product
14−types.
10+This library uses `GHC.Generics` to derive efficient optics (traversals, lenses
11+and prisms) for algebraic data types in a type-directed way, with a focus on
12+good type inference and error messages when possible.
13+
14+The derived optics use the so-called van Laarhoven representation, thus are
15+fully interoperable with the combinators found in mainstream lens libraries.
16+
17+Examples can be found in the `examples` and `tests` folders.
18+
19+The library is described in the paper:
20+> Csongor Kiss, Matthew Pickering, and Nicolas Wu. 2018. Generic deriving of generic traversals. Proc. ACM Program. Lang. 2, ICFP, Article 85 (July 2018), 30 pages. DOI: https://doi.org/10.1145/3236780
21+
22+Table of contents
23+=================
24+
25+* [Preliminaries](#preliminaries)
26+* [Taxonomy of optics](#taxonomy-of-optics)
27+ * [Lenses](#lenses)
28+ * [By name](#by-name)
29+ * [By position](#by-position)
30+ * [By type](#by-type)
31+ * [By structure](#by-structure)
32+ * [Traversals](#traversals)
33+ * [By type](#by-type-1)
34+ * [By parameter](#by-parameter)
35+ * [By constraint](#by-constraint)
36+ * [Prisms](#prisms)
37+ * [By name](#by-name-1)
38+ * [By type](#by-type-2)
39+* [Performance](#performance)
40+ * [Inspection testing](#inspection-testing)
41+ * [Benchmarks](#benchmarks)
42+* [Contributors](#contributors)
43+
44+# Preliminaries
45+A typical module using `generic-lens` will usually have the following
46+extensions turned on:
47+```haskell
48+{-# LANGUAGE AllowAmbiguousTypes #-}
49+{-# LANGUAGE DataKinds #-}
50+{-# LANGUAGE DeriveGeneric #-}
51+{-# LANGUAGE DuplicateRecordFields #-}
52+{-# LANGUAGE FlexibleContexts #-}
53+{-# LANGUAGE NoMonomorphismRestriction #-}
54+{-# LANGUAGE TypeApplications #-}
1555
16−This is made possible by GHC 8's new Generics API, which provides metadata
17−at the type-level (previously only value-level metadata was available).
56+```
1857
19−Examples can be found in the `examples` folder. This library makes heavy use of
20−[Visible Type Applications](https://ghc.haskell.org/trac/ghc/wiki/TypeApplication).
58+# Taxonomy of optics
59+Here is a comprehensive list of the optics exposed by `generic-lens`. The
60+combinators each allow a different way of identifying certain parts of
61+algebraic data types.
2162
2263## Lenses
2364
24−### Record fields
65+A lens identifies exactly one part of a product type, and allows querying and
66+updating it.
2567
26−Record fields can be accessed by their label:
68+### By name
2769
2870```haskell
2971data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
@@ -32,25 +74,83 @@ sally :: Person
3274sally = Person "Sally" 25
3375```
3476
35−```haskell
36−>>> getField @"age" sally
37−25
38−
39−>>> setField @"age" 26 sally
40−Person {name = "Sally", age = 26}
77+Record fields can be accessed by their label using the `field` lens.
4178
79+```haskell
4280>>> sally ^. field @"name"
4381"Sally"
4482
4583>>> sally & field @"name" .~ "Tamas"
4684Person {name = "Tamas", age = 25}
85+```
86+Here we use [visible type application](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#visible-type-application)
87+to specify which field we're interested in, and use the `^.` and `.~` combinators from a lens library
88+([lens](https://hackage.haskell.org/package/lens), [microlens](https://hackage.haskell.org/package/microlens), etc.)
89+to query and update the field.
4790
91+Or for standalone use, the `getField` and `setField` functions can be used instead.
92+```haskell
93+>>> getField @"age" sally
94+25
95+
96+>>> setField @"age" 26 sally
97+Person {name = "Sally", age = 26}
98+```
99+
100+When a non-existent field is requested, the library generates a helpful type error:
101+```haskell
48102>>> sally ^. field @"pet"
49103error:
50− • The type Person does not contain a field named "pet"
104+ • The type Person does not contain a field named 'pet'
105+```
106+
107+For types with multiple constructors, we can still use `field` as long as all constructors contain the required field
108+```haskell
109+data Two
110+ = First { wurble :: String, banana :: Int }
111+ | Second { wurble :: String }
112+ deriving (Generic, Show)
113+
114+>>> Second "woops" ^. field @"wurble"
115+"woops"
116+>>> Second "woops" ^. field @"banana"
117+ ...
118+ • Not all constructors of the type Two
119+ contain a field named 'banana'.
120+ The offending constructors are:
121+ • Second
122+ ...
123+```
124+
125+The type of `field` is
126+```haskell
127+field :: HasField name s t a b => Lens s t a b
51128```
129+Therefore it allows polymorphic (type-changing) updates, when the accessed field mentions type parameters.
52130
53−### Positional fields
131+```haskell
132+data Foo f a = Foo
133+ { foo :: f a
134+ } deriving (Generic, Show)
135+
136+foo1 :: Foo Maybe Int
137+foo1 = Foo (Just 10)
138+
139+-- |
140+-- >>> foo2
141+-- Foo {foo = ["10"]}
142+foo2 :: Foo [] String
143+foo2 = foo1 & field @"foo" %~ (maybeToList . fmap show)
144+```
145+This example shows that higher-kinded parameters can also be changed (`Maybe`
146+-> `[]`). We turn a `Foo Maybe Int` into a `Foo [] String` by turning the inner
147+`Maybe Int` into a `[String]`.
148+
149+With `DuplicateRecordFields`, multiple data types can share the same field
150+name, and the `field` lens works in this case too. No more field name
151+prefixing!
152+
153+### By position
54154
55155Fields can be accessed by their position in the data structure (index starting at 1):
56156
@@ -63,12 +163,6 @@ polygon = Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 7 (-2))
63163```
64164
65165```haskell
66−>>> getPosition @2 polygon
67−Point 2 4 2
68−
69−>>> setPosition @1 (Point 26 5 3) polygon
70−Polygon (Point 26 5 3) (Point 2 4 2) (Point 5 7 (-2))
71−
72166>>> polygon ^. position @1 . position @2
731675
74168
@@ -80,49 +174,34 @@ error:
80174 • The type Polygon does not contain a field at position 10
81175```
82176
83−Since tuples are an instance of `Generic`, they also have positional lenses:
177+Since tuples are an instance of `Generic`, the positional lens readily works:
84178
85179```haskell
86180>>> (("hello", True), 5) ^. position @1 . position @2
87181True
182+>>> (("hello", True, "or"), 5, "even", "longer") ^. position @1 . position @2
183+True
88184```
89185
90−### Typed fields
186+### By type
91187
92188Fields can be accessed by their type in the data structure, assuming that this
93189type is unique:
94190
95191```haskell
96−data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
97−data Point = Point Int Int Int deriving (Generic, Show)
98−
99−sally :: Person
100−sally = Person "Sally" 25
101−
102−point :: Point
103−point = Point 1 2 3
104−```
105−
106−```haskell
107−>>> getTyped @String sally
192+>>> sally ^. typed @String
108193"Sally"
109194
110195>>> setTyped @Int sally 26
111196Person {name = "Sally", age = 26}
112−
113−>>> point ^. typed @Int
114−error:
115− • The type Point contains multiple values of type Int; the choice of value is thus ambiguous
116−
117−>>> point & typed @String .~ "Point"
118−error:
119− • The type Point does not contain a value of type [Char]
120197```
121198
122−### Structural subtyping
199+### By structure
123200
124−A record is a (structural) `subtype' of another, if its fields are a superset of
125−those of the other.
201+The `super` lens generalises the `field` lens to focus on a collection rather
202+than a single field.
203+We can say that a record is a (structural) `subtype' of another, if its fields
204+are a superset of those of the other.
126205
127206```haskell
128207data Human = Human
@@ -141,16 +220,10 @@ human = Human {name = "Tunyasz", age = 50, address = "London"}
141220```
142221
143222```haskell
144−
145−>>> upcast human :: Animal
223+>>> human ^. super @Animal
146224Animal {name = "Tunyasz", age = 50}
147225
148−-- 'smash' plug the smaller structure into the larger one
149−>>> smash (Animal "dog" 10) human
150−Human {name = "dog", age = 10, address = "London"}
151−
152−-- 'super' is a lens that focuses on a subrecord of a larger record:
153−>>> human ^. super @Animal
226+>>> upcast human :: Animal
154227Animal {name = "Tunyasz", age = 50}
155228```
156229
@@ -165,9 +238,96 @@ growUp (Animal name age) = Animal name (age + 50)
165238Human {name = "Tunyasz", age = 60, address = "London"}
166239```
167240
241+## Traversals
242+
243+Traversals allow multiple values to be queried or updated at the same time.
244+
245+As a running example, consider a data type of weighted trees. There are two
246+type parameters, which correspond to the type of elements and weights in the
247+tree:
248+```haskell
249+data WTree a w
250+ = Leaf a
251+ | Fork (WTree a w) (WTree a w)
252+ | WithWeight (WTree a w) w
253+ deriving (Generic, Show)
254+
255+mytree :: WTree Int Int
256+mytree = Fork (WithWeight (Leaf 42) 1)
257+ (WithWeight (Fork (Leaf 88) (Leaf 37)) 2)
258+```
259+
260+### By type
261+Focus on all values of a given type.
262+
263+```haskell
264+types :: HasTypes s a => Traversal' s a
265+```
266+
267+```haskell
268+>>> toListOf (types @Int) myTree
269+[42,1,88,37,2]
270+```
271+
272+Note that this traversal is "deep": the subtrees are recursively traversed.
273+
274+### By parameter
275+As the above example shows, the `types` traversal is limited in that it cannot
276+distinguish between the two types of `Int`s: the weights and the values.
277+
278+Instead, the `param` traversal allows specifying types that correspond to a
279+certain type parameter.
280+```haskell
281+param :: HasParam pos s t a b => Traversal s t a b
282+```
283+
284+```haskell
285+>>> toListOf (param @1) myTree
286+[42,88,37]
287+```
288+
289+Here, the numbering starts from 0, with 0 being the rightmost parameter.
290+Because `param` is guaranteed to focus on parametric values, it allows the type
291+to be changed as well.
292+
293+For example, we can implement `Functor` for `WTree` as simply as:
294+
295+```haskell
296+instance Functor (WTree a) where
297+ fmap = over (param @0)
298+```
299+
300+### By constraint
301+The most general type of traversal: we can apply a given function to every
302+value in a structure, by requiring that all values have an instance for some
303+type class.
304+
305+```haskell
306+constraints :: HasConstraints c s t => Applicative g => (forall a b . c a b => a -> g b) -> s -> g t
307+constraints' :: HasConstraints' c s => Applicative g => (forall a . c a => a -> g a) -> s -> g s
308+```
309+
310+Consider the `Numbers` type, which contains three different numeric types:
311+```haskell
312+data Numbers = Numbers Int Float Double
313+ deriving (Show, Generic)
314+
315+numbers = Numbers 10 20.0 30.0
316+```
317+
318+With `constraints'`, we can uniformly add 20 to each number in one go:
319+```haskell
320+>>> constraints' @Num (\x -> pure (x + 20)) numbers
321+Numbers 30 40.0 50.0
322+```
323+
168324## Prisms
169325
170−### Named constructors
326+A prism focuses on one part of a sum type (which might not be present). Other
327+than querying the type, they can be "turned around" to inject the data into the
328+sum (like a constructor).
329+
330+### By name
171331
172332Constructor components can be accessed using the constructor's name:
173333
@@ -189,19 +349,20 @@ Just (MkDog {name = "Shep", age = 4})
189349
190350>>> shep ^? _Ctor @"Cat"
191351Nothing
352+```
353+
354+Constructors with multiple fields can be focused as a tuple
192355
356+```
193357>>> mog ^? _Ctor @"Cat"
194358Just ("Mog",5)
195359
196360>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
197361Cat "Garfield" 6
198362
199−>>> donald ^? _Ctor @"Giraffe"
200−error:
201− • The type Animal does not contain a constructor named "Giraffe"
202363```
203364
204−### Typed constructors
365+### By type
205366
206367Constructor components can be accessed using the component's type, assuming
207368that this type is unique:
@@ -211,10 +372,10 @@ type Name = String
211372type Age = Int
212373
213374data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
214−data Animal = Dog Dog | Cat (Name, Age) | Duck Age deriving (Generic, Show)
375+data Animal = Dog Dog | Cat Name Age | Duck Age deriving (Generic, Show)
215376
216377shep = Dog (MkDog "Shep" 4)
217−mog = Cat ("Mog", 5)
378+mog = Cat "Mog" 5
218379donald = Duck 4
219380```
220381
@@ -228,6 +389,9 @@ Just (MkDog {name = "Shep", age = 4})
228389>>> donald ^? _Typed @Age
229390Just 4
230391
392+>>> mog ^? _Typed @(Name, Age)
393+("Mog", 5)
394+
231395>>> donald ^? _Typed @Float
232396error:
233397 • The type Animal does not contain a constructor whose field is of type Float
@@ -236,7 +400,19 @@ error:
236400Duck 6
237401```
238402
239−## Contributors
403+# Performance
404+The runtime characteristics of the derived optics is in most cases identical at
405+`-O1`, in some cases only slightly slower than the hand-written version. This
406+is thanks to GHC's optimiser eliminating the generic overhead.
407+
408+The
409+[inspection-testing](https://hackage.haskell.org/package/inspection-testing)
410+library is used to ensure (see [here](test/Spec.hs)) that everything gets
411+inlined away.
412+
413+There is also a [benchmark suite](https://github.com/mpickering/generic-lens-benchmarks) with larger, real-world examples.
414+# Contributors
240415
416++ [Matthew Pickering](https://github.com/mpickering)
241417+ [Toby Shaw](https://github.com/TobyShaw)
242418+ [Will Jones](https://github.com/lunaris)
cabal.haskell-cinew file+5
@@ -0,0 +1,5 @@
1+-- inspection tests fail
2+tests: <8.6
3+
4+-- blocked on vector-th-unbox
5+benchmarks: <8.8
cabal.projectnew file+2
@@ -0,0 +1,2 @@
1+packages: generic-lens.cabal
2+write-ghc-environment-files: always
examples/Biscuits.hsnew file+150
@@ -0,0 +1,150 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE DeriveGeneric #-}
3+{-# LANGUAGE DuplicateRecordFields #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE NoMonomorphismRestriction #-}
6+{-# LANGUAGE TypeApplications #-}
7+
8+module Main where
9+
10+import Control.Lens
11+import Data.Maybe (maybeToList)
12+import Data.Generics.Product
13+import Data.Generics.Sum
14+import GHC.Generics (Generic)
15+
16+-- $setup
17+--
18+-- >>> :set -XTypeApplications
19+-- >>> :set -XDataKinds
20+-- >>> :set -XFlexibleContexts
21+
22+main :: IO ()
23+main = putStrLn "hello"
24+
25+data Item = Item
26+ { name :: String
27+ , cost :: Cost
28+ } deriving (Generic, Show)
29+
30+newtype Cost = Cost Double deriving (Generic, Show)
31+
32+data Invoice p = Invoice
33+ { item :: Item
34+ , name :: String
35+ , number :: Int
36+ , priority :: p
37+ } deriving (Generic, Show)
38+
39+data Orders = Orders [ Invoice Int ] [ Invoice (Int, Double) ]
40+ deriving (Generic, Show)
41+
42+-- |
43+-- >>> view (field @"name") bourbon
44+-- "Bourbon"
45+-- >>> bourbon & field @"cost" .~ Cost 110
46+-- Item {name = "Bourbon", cost = Cost 110.0}
47+--
48+-- >>> bourbon & field @"cost" %~ (\(Cost c) -> (Cost (c + 5)))
49+-- Item {name = "Bourbon", cost = Cost 105.0}
50+--
51+-- >>> Invoice bourbon "Johnny" 2 2 & field @"priority" %~ (\i -> (i, 0))
52+-- Invoice {item = Item {name = "Bourbon", cost = Cost 100.0}, name = "Johnny", number = 2, priority = (2,0)}
53+--
54+-- >>> view (field @"weight") bourbon
55+-- ...
56+-- ... The type Item does not contain a field named 'weight'.
57+-- ...
58+--
59+-- >>> bourbon & typed @Cost .~ Cost 200
60+-- Item {name = "Bourbon", cost = Cost 200.0}
61+--
62+-- >>> bourbon & typed %~ ("Chocolate " ++)
63+-- Item {name = "Chocolate Bourbon", cost = Cost 100.0}
64+--
65+-- >>> view (position @1) (42, "foo")
66+-- 42
67+-- >>> view (position @1) (42, "foo", False)
68+-- 42
69+-- >>> view (position @2) orders
70+-- [Invoice {item = Item {name = "Bourbon", cost = Cost 100.0}, name = "George", number = 2, priority = (0,3.0)}]
71+--
72+-- >>> view (position @2) orders
73+-- [Invoice {item = Item {name = "Bourbon", cost = Cost 100.0}, name = "George", number = 2, priority = (0,3.0)}]
74+--
75+-- >>> view (position @3) orders
76+-- ...
77+-- ... The type Orders does not contain a field at position 3
78+-- ...
79+--
80+-- >>> view (super @Item) (WItem "Bourbon" (Cost 2000) (Weight 0.03))
81+-- Item {name = "Bourbon", cost = Cost 2000.0}
82+--
83+-- >>> (WItem "Bourbon+" (Cost 500) (Weight 0.03)) & super @Item .~ bourbon
84+-- WItem {name = "Bourbon", cost = Cost 100.0, weight = Weight 3.0e-2}
85+--
86+-- >>> DInt 1 ^? _Ctor @"DInt"
87+-- Just 1
88+--
89+-- >>> _Typed # (False, "wurble") :: D
90+-- DPair False "wurble"
91+--
92+-- >>> EChar 'a' ^? _Sub @D
93+-- Nothing
94+--
95+-- >>> _Sub # DInt 10 :: E
96+-- EInt 10
97+bourbon :: Item
98+bourbon = Item "Bourbon" (Cost 100)
99+
100+orders :: Orders
101+orders = Orders [Invoice bourbon "Earl" 1 0 , Invoice bourbon "Johnny" 2 2]
102+ [Invoice bourbon "George" 2 (0, 3)]
103+
104+nameOfItem :: Invoice p -> String
105+nameOfItem = view (field @"item" . field @"name")
106+
107+thankYou :: Orders -> Orders
108+thankYou = over (types @Cost) (\(Cost c) -> Cost (c * 0.85))
109+
110+thankYouPriority :: Orders -> Orders
111+thankYouPriority = over (position @2 . types @Cost) (\(Cost c) -> Cost (c * 0.85))
112+
113+upgrade :: Double -> Invoice Int -> Invoice (Int, Double)
114+upgrade bribe invoice = over (param @0) (\i -> (i, bribe)) invoice
115+
116+audit :: Orders -> [Item]
117+audit = toListOf (types @Item)
118+
119+newtype Weight = Weight Double deriving (Generic, Show)
120+data WeighedItem = WItem
121+ { name :: String
122+ , cost :: Cost
123+ , weight :: Weight
124+ } deriving (Generic, Show)
125+
126+data D = DInt Int | DPair Bool String
127+ deriving (Generic, Show)
128+
129+data E = EInt Int | EPair Bool String | EChar Char
130+ deriving (Generic, Show)
131+
132+costInc :: HasTypes t Cost => t -> t
133+costInc = over (types @Cost) (\(Cost c) -> Cost (c + 5))
134+
135+modifyPriority :: (Int -> Int) -> Invoice Int -> Invoice Int
136+modifyPriority = over (types @Int)
137+
138+treeIncParam :: HasParam 0 s s Int Int => s -> s
139+treeIncParam = over (param @0) (+ 1)
140+
141+instance Functor Invoice where
142+ fmap = over (param @0)
143+
144+data Numbers = Numbers Int Float Double
145+ deriving (Show, Generic)
146+
147+-- |
148+-- >>> constraints' @Num (\x -> pure (x + 20)) numbers
149+-- Numbers 30 40.0 50.0
150+numbers = Numbers 10 20.0 30.0
examples/Examples.hs+98−9
@@ -1,17 +1,32 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE DeriveGeneric #-}
3−{-# LANGUAGE DuplicateRecordFields #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE TypeApplications #-}
6−{-# LANGUAGE UndecidableInstances #-}
7−
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE DeriveGeneric #-}
4+{-# LANGUAGE DuplicateRecordFields #-}
5+{-# LANGUAGE FlexibleContexts #-}
6+{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE NoMonomorphismRestriction #-}
8+{-# LANGUAGE OverloadedLabels #-}
9+{-# LANGUAGE PartialTypeSignatures #-}
10+{-# LANGUAGE Rank2Types #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE UndecidableInstances #-}
14+{-# OPTIONS_GHC -Wno-missing-signatures #-}
815{-# OPTIONS_GHC -fno-warn-unused-imports #-}
916
1017module Examples where
1118
12−import GHC.Generics
19+import Data.Function ((&))
20+import Data.Generics.Internal.VL.Lens
1321import Data.Generics.Product
14−--import Control.Lens
22+import Data.Generics.Sum
23+import GHC.Generics
24+import Data.Generics.Labels
25+import Data.Generics.Internal.VL.Iso
26+import Data.Generics.Internal.VL.Prism
27+import Data.Generics.Internal.Profunctor.Lens
28+import Data.Generics.Internal.Profunctor.Iso
29+import Data.Generics.Internal.Profunctor.Prism
1530
1631data Animal = Animal
1732 { name :: String
@@ -41,3 +56,77 @@ data MyRecord = MyRecord { field1 :: Int, field2 :: String } deriving Generic
4156
4257--g :: Subtype s MyRecord => s -> String
4358--g s = s ^. super @MyRecord . label @"field2"
59+
60+data Test a b = Test { fieldInt :: Int, fieldA :: a, fieldB :: b } deriving (Generic, Show)
61+
62+-- changedA :: Test Int String
63+-- >>> changedA
64+-- Test {fieldInt = 10, fieldA = 10, fieldB = "world"}
65+changedA = Test 10 "hello" "world" & field @"fieldA" .~ (10 :: Int)
66+
67+-- changedB :: Test String Int
68+-- >>> changedB
69+-- Test {fieldInt = 10, fieldA = "hello", fieldB = 10}
70+changedB = (Test 10 "hello" "world") & field @"fieldB" .~ (10 :: Int)
71+
72+--changedInt = set (field @"fieldInt") ("hello") (Test 10 "hello" "world")
73+-- type error
74+
75+data Animal2 a
76+ = Dog (Dog a)
77+ | Cat Name Age
78+ | Duck Age
79+ deriving (Generic, Show)
80+
81+data Dog a
82+ = MkDog
83+ { name :: Name
84+ , age :: Age
85+ , fieldA :: a
86+ }
87+ deriving (Generic, Show)
88+type Name = String
89+type Age = Int
90+dog :: Animal2 Int
91+dog = Dog (MkDog "Shep" 3 30)
92+
93+-- TODO: the error message for this case is ugly
94+-- data Dog a
95+-- = MkDog
96+-- { name :: Name
97+-- , age :: Age
98+-- , fieldA :: a
99+-- , fieldA' :: a
100+-- }
101+-- deriving (Generic, Show)
102+
103+--dog' :: Animal2 String
104+dog' = dog & _Ctor @"Dog" . field @"fieldA" .~ "now it's a String"
105+
106+stuff ::
107+ ( HasPosition 15 s t a String
108+ , HasField "test" s' t' a' b'
109+ , HasField "bar" a' b' s t
110+ ) => s' -> t'
111+stuff r = r & field @"test" . field @"bar" . position @15 .~ "hello"
112+
113+stuff' ::
114+ ( HasPosition 15 s t a String
115+ , HasField "test" s' t' a' b'
116+ , HasField "bar" a' b' s t
117+ ) => s' -> t'
118+stuff' r = r & #test . #bar . position @15 .~ "hello"
119+
120+data Foo m s = Foo
121+ { foo1 :: m s
122+ , foo2 :: [s]
123+ } deriving Generic
124+
125+modifyFoo2 :: Foo (Either String) Int -> Foo Maybe Int
126+modifyFoo2 x = x & field @"foo1" .~ pure (1 :: Int)
127+
128+data Bar a b = Bar
129+ { barField :: (a, b)
130+ } deriving Generic
131+
132+modifiedBar = (Bar ("hello", "world")) & field @"barField" .~ ('c', 1 :: Int)
examples/StarWars.hs+1−1
@@ -59,7 +59,7 @@ c3po = Droid
5959 , primaryFunction = "protocol and human relations"
6060 }
6161
62−getName :: HasField "name" a r => r -> a
62+getName :: HasField' "name" r a => r -> a
6363getName = getField @"name"
6464
6565-- upcast :: Subtype a b => a -> b
examples/doctest.hsnew file+7
@@ -0,0 +1,7 @@
1+import Test.DocTest
2+main
3+ = doctest
4+ [ "-iexamples"
5+ , "-isrc"
6+ , "examples/Biscuits.hs"
7+ ]
generic-lens.cabal+112−17
@@ -1,7 +1,7 @@
11name: generic-lens
2−version: 0.4.0.1
3−synopsis: Generic data-structure operations exposed as lenses.
4−description: This package uses the GHC 8 Generic representation to derive various operations on data structures with a lens interface, including structural subtype relationship between records and positional indexing into arbitrary product types.
2+version: 1.2.0.1
3+synopsis: Generically derive traversals, lenses and prisms.
4+description: This library uses GHC.Generics to derive efficient optics (traversals, lenses and prisms) for algebraic data types in a type-directed way, with a focus on good type inference and error messages when possible.
55
66homepage: https://github.com/kcsongor/generic-lens
77license: BSD3
@@ -11,67 +11,161 @@ maintainer: kiss.csongor.kiss@gmail.com
1111category: Generics, Records, Lens
1212build-type: Simple
1313cabal-version: >= 1.10
14+Tested-With: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.1, GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.1
1415
1516extra-source-files: ChangeLog.md
1617 , examples/StarWars.hs
1718 , examples/Examples.hs
1819 , README.md
1920
21+flag dump-core
22+ description: Dump HTML for the core generated by GHC during compilation
23+ default: False
24+
2025library
21− exposed-modules: Data.Generics.Product
26+ exposed-modules: Data.Generics.Wrapped
27+ , Data.Generics.Product
2228 , Data.Generics.Product.Any
2329 , Data.Generics.Product.Fields
30+ , Data.Generics.Product.Param
2431 , Data.Generics.Product.Positions
2532 , Data.Generics.Product.Subtype
2633 , Data.Generics.Product.Typed
34+ , Data.Generics.Product.Types
35+ , Data.Generics.Product.Constraints
36+ , Data.Generics.Product.HList
37+ , Data.Generics.Labels
38+ , Data.Generics.Internal.GenericN
2739
2840 , Data.Generics.Sum
2941 , Data.Generics.Sum.Any
3042 , Data.Generics.Sum.Constructors
3143 , Data.Generics.Sum.Typed
3244 , Data.Generics.Sum.Subtype
33− , Data.Generics.Internal.Lens
45+ , Data.Generics.Internal.Profunctor.Lens
46+ , Data.Generics.Internal.Profunctor.Prism
47+ , Data.Generics.Internal.Profunctor.Iso
48+ , Data.Generics.Internal.VL.Lens
49+ , Data.Generics.Internal.VL.Traversal
50+ , Data.Generics.Internal.VL.Prism
51+ , Data.Generics.Internal.VL.Iso
52+
53+ , Data.GenericLens.Internal
3454
3555 other-modules: Data.Generics.Internal.Families
36− , Data.Generics.Internal.Families.Count
56+ , Data.Generics.Internal.Families.Changing
57+ , Data.Generics.Internal.Families.Collect
3758 , Data.Generics.Internal.Families.Has
38− , Data.Generics.Internal.HList
59+ , Data.Generics.Internal.Errors
60+ , Data.Generics.Internal.Void
3961
4062 , Data.Generics.Sum.Internal.Constructors
4163 , Data.Generics.Sum.Internal.Typed
4264 , Data.Generics.Sum.Internal.Subtype
4365
44− , Data.Generics.Internal.Families
45− , Data.Generics.Internal.Families.Count
46− , Data.Generics.Internal.Families.Has
47− , Data.Generics.Internal.HList
48− , Data.Generics.Product.Internal.Fields
4966 , Data.Generics.Product.Internal.Positions
67+ , Data.Generics.Product.Internal.GLens
5068 , Data.Generics.Product.Internal.Subtype
51− , Data.Generics.Product.Internal.Typed
5269
53− build-depends: base >= 4.9 && <= 5.0
54− , profunctors >= 5.0 && <= 6.0
70+ , Data.Generics.Product.Internal.Constraints
71+ , Data.Generics.Product.Internal.HList
72+
73+ build-depends: base >= 4.9 && < 5
74+ , profunctors >= 5.0 && < 6.0
75+ , tagged >= 0.8 && < 1
76+ , text >= 1.2 && < 1.3
5577
5678 hs-source-dirs: src
5779 default-language: Haskell2010
5880 ghc-options: -Wall
81+-- if flag(dump-core)
82+-- build-depends: dump-core
83+-- ghc-options: -fplugin=DumpCore -fplugin-opt DumpCore:core-html
5984
6085source-repository head
6186 type: git
6287 location: https://github.com/kcsongor/generic-lens
6388
89+test-suite generic-lens-examples
90+ type: exitcode-stdio-1.0
91+ hs-source-dirs: examples
92+ main-is: Biscuits.hs
93+
94+ build-depends: base >= 4.9 && <= 5.0
95+ , generic-lens
96+ , lens
97+ , profunctors
98+ , HUnit
99+
100+ default-language: Haskell2010
101+
64102test-suite generic-lens-test
65103 type: exitcode-stdio-1.0
66104 hs-source-dirs: test
67105 main-is: Spec.hs
106+ other-modules: Util Test24 Test25 Test40 Test62 Test63 CustomChildren
107+
108+ build-depends: base >= 4.9 && <= 5.0
109+ , generic-lens
110+ , lens
111+ , profunctors
112+ , inspection-testing >= 0.2
113+ , HUnit
114+
115+ default-language: Haskell2010
116+ ghc-options: -Wall
117+ if flag(dump-core)
118+ build-depends: dump-core
119+ ghc-options: -fplugin=DumpCore -fplugin-opt DumpCore:core-html
120+
121+test-suite generic-lens-bifunctor
122+ type: exitcode-stdio-1.0
123+ hs-source-dirs: test
124+ main-is: Bifunctor.hs
125+
126+ build-depends: base >= 4.9 && <= 5.0
127+ , generic-lens
128+ , lens
129+ , HUnit
130+
131+ default-language: Haskell2010
132+ ghc-options: -Wall
133+ if flag(dump-core)
134+ build-depends: dump-core
135+ ghc-options: -fplugin=DumpCore -fplugin-opt DumpCore:core-html
136+
137+test-suite generic-lens-syb-tree
138+ type: exitcode-stdio-1.0
139+ hs-source-dirs: test/syb
140+ main-is: Tree.hs
68141
69142 build-depends: base >= 4.9 && <= 5.0
70143 , generic-lens
71− , ghc-proofs >= 0.1.1
144+ , lens
145+ , profunctors
146+ , HUnit
72147
73148 default-language: Haskell2010
74149 ghc-options: -Wall
150+ if flag(dump-core)
151+ build-depends: dump-core
152+ ghc-options: -fplugin=DumpCore -fplugin-opt DumpCore:core-html
153+
154+test-suite doctests
155+ default-language: Haskell2010
156+ type: exitcode-stdio-1.0
157+ ghc-options: -threaded
158+ main-is: doctest.hs
159+ build-depends: base >4 && <5, doctest
160+ hs-source-dirs: test
161+
162+test-suite examples-doctests
163+ default-language: Haskell2010
164+ type: exitcode-stdio-1.0
165+ ghc-options: -threaded
166+ main-is: doctest.hs
167+ build-depends: base >4 && <5, doctest
168+ hs-source-dirs: examples
75169
76170benchmark generic-lens-bench
77171 type: exitcode-stdio-1.0
@@ -82,9 +176,10 @@ benchmark generic-lens-bench
82176
83177 , base >= 4.9 && <= 5.0
84178 , QuickCheck
85− , criterion
179+ , criterion >= 1.3.0.0
86180 , deepseq
87181 , lens
88182
89183 default-language: Haskell2010
90184 ghc-options: -Wall
185+
src/Data/GenericLens/Internal.hsnew file+64
@@ -0,0 +1,64 @@
1+-----------------------------------------------------------------------------
2+-- |
3+-- Module : Data.GenericLens.Internal
4+-- Copyright : (C) 2019 Csongor Kiss
5+-- License : BSD3
6+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
7+-- Stability : experimental
8+-- Portability : non-portable
9+--
10+-- The library internals are exposed through this module. Please keep
11+-- in mind that everything here is subject to change irrespective of
12+-- the the version numbers.
13+-----------------------------------------------------------------------------
14+
15+module Data.GenericLens.Internal
16+ ( module Data.Generics.Internal.Families
17+ , module Data.Generics.Internal.Families.Changing
18+ , module Data.Generics.Internal.Families.Collect
19+ , module Data.Generics.Internal.Families.Has
20+ , module Data.Generics.Internal.Void
21+ , module Data.Generics.Internal.Errors
22+
23+ , module Data.Generics.Sum.Internal.Constructors
24+ , module Data.Generics.Sum.Internal.Typed
25+ , module Data.Generics.Sum.Internal.Subtype
26+
27+ , module Data.Generics.Product.Internal.Positions
28+ , module Data.Generics.Product.Internal.GLens
29+ , module Data.Generics.Product.Internal.Subtype
30+ , module Data.Generics.Product.Internal.Constraints
31+ , module Data.Generics.Product.Internal.HList
32+
33+ , module Data.Generics.Internal.GenericN
34+
35+ -- * van Laarhoven optics
36+ , module Data.Generics.Internal.VL.Iso
37+ , module Data.Generics.Internal.VL.Lens
38+ , module Data.Generics.Internal.VL.Prism
39+ , module Data.Generics.Internal.VL.Traversal
40+ ) where
41+
42+import Data.Generics.Internal.Families
43+import Data.Generics.Internal.Families.Changing
44+import Data.Generics.Internal.Families.Collect
45+import Data.Generics.Internal.Families.Has
46+import Data.Generics.Internal.Void
47+import Data.Generics.Internal.Errors
48+
49+import Data.Generics.Sum.Internal.Constructors
50+import Data.Generics.Sum.Internal.Typed
51+import Data.Generics.Sum.Internal.Subtype
52+
53+import Data.Generics.Product.Internal.Positions
54+import Data.Generics.Product.Internal.GLens
55+import Data.Generics.Product.Internal.Subtype
56+import Data.Generics.Product.Internal.Constraints
57+import Data.Generics.Product.Internal.HList
58+
59+import Data.Generics.Internal.GenericN
60+
61+import Data.Generics.Internal.VL.Iso
62+import Data.Generics.Internal.VL.Lens
63+import Data.Generics.Internal.VL.Prism
64+import Data.Generics.Internal.VL.Traversal
src/Data/GenericLens/Internal.hs~new file

No changes to the content.

src/Data/Generics/Internal/Errors.hsnew file+59
@@ -0,0 +1,59 @@
1+{-# LANGUAGE UndecidableInstances #-}
2+{-# LANGUAGE TypeFamilies #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE PolyKinds #-}
5+{-# LANGUAGE DataKinds #-}
6+{-# LANGUAGE TypeOperators #-}
7+{-# LANGUAGE KindSignatures #-}
8+
9+-------------------------------------------------------------------------------
10+---- |
11+---- Module : Data.Generics.Internal.Errors
12+---- Copyright : (C) 2019 Csongor Kiss
13+---- License : BSD3
14+---- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
15+---- Stability : experimental
16+---- Portability : non-portable
17+----
18+---- Provide more legible type errors as described in
19+---- (https://kcsongor.github.io/report-stuck-families/)[this blog post].
20+-------------------------------------------------------------------------------
21+
22+module Data.Generics.Internal.Errors
23+ ( NoGeneric
24+ , Defined
25+ , Defined_list
26+
27+ , QuoteType
28+ , PrettyError
29+ ) where
30+
31+import GHC.Generics
32+import GHC.TypeLits
33+import Data.Kind
34+
35+type family NoGeneric (a :: Type) (ctxt :: [ErrorMessage]) :: Constraint where
36+ NoGeneric a ctxt = PrettyError ('Text "No instance for " ':<>: QuoteType (Generic a)
37+ ': ctxt)
38+type family PrettyError (ctxt :: [ErrorMessage]) :: k where
39+ PrettyError '[] = TypeError ('Text "")
40+ PrettyError (c ': cs) = TypeError ('Text "| " ':<>: c ':$$: PrettyLines cs)
41+
42+type family PrettyLines (ctxt :: [ErrorMessage]) :: ErrorMessage where
43+ PrettyLines '[] = 'Text ""
44+ PrettyLines (c ': cs) = 'Text "| " ':<>: c ':$$: PrettyLines cs
45+
46+type family Defined (break :: Type -> Type) (err :: Constraint) (a :: k) :: k where
47+ Defined Void1 _ _ = Any
48+ Defined _ _ k = k
49+
50+type family Defined_list (break :: [*]) (err :: Constraint) (a :: k) :: k where
51+ Defined_list '[Void] _ _ = Any
52+ Defined_list _ _ k = k
53+
54+data Void1 a
55+data Void
56+type family Any :: k
57+
58+type family QuoteType (typ :: k) :: ErrorMessage where
59+ QuoteType typ = 'Text "‘" ':<>: 'ShowType typ ':<>: 'Text "’"
src/Data/Generics/Internal/Families.hs+21−3
@@ -1,7 +1,15 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE KindSignatures #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE TypeOperators #-}
8+
19-----------------------------------------------------------------------------
210-- |
311-- Module : Data.Generics.Internal.Families
4−-- Copyright : (C) 2017 Csongor Kiss
12+-- Copyright : (C) 2019 Csongor Kiss
513-- License : BSD3
614-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
715-- Stability : experimental
@@ -10,7 +18,17 @@
1018-----------------------------------------------------------------------------
1119module Data.Generics.Internal.Families
1220 ( module Families
21+ , ShowSymbols
1322 ) where
1423
15−import Data.Generics.Internal.Families.Count as Families
16−import Data.Generics.Internal.Families.Has as Families
24+import Data.Generics.Internal.Families.Collect as Families
25+import Data.Generics.Internal.Families.Has as Families
26+import Data.Generics.Internal.Families.Changing as Families
27+
28+import GHC.TypeLits (ErrorMessage (..), Symbol)
29+
30+type family ShowSymbols (ctors :: [Symbol]) :: ErrorMessage where
31+ ShowSymbols '[]
32+ = 'Text ""
33+ ShowSymbols (c ': cs)
34+ = 'Text "• " ':<>: 'Text c ':$$: ShowSymbols cs
src/Data/Generics/Internal/Families/Changing.hsnew file+161
@@ -0,0 +1,161 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE PolyKinds #-}
7+{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE TypeOperators #-}
9+{-# LANGUAGE UndecidableInstances #-}
10+
11+module Data.Generics.Internal.Families.Changing
12+ ( Indexed
13+ , Infer
14+ , PTag (..)
15+ , P
16+ , LookupParam
17+ , ArgAt
18+ , ArgCount
19+ , UnifyHead
20+ ) where
21+
22+import GHC.TypeLits (Nat, type (-), type (+), TypeError, ErrorMessage (..))
23+
24+{-
25+ Note [Changing type parameters]
26+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27+
28+ To get good type inference for type-changing lenses, we want to be able
29+ map the field's type back to the type argument it corresponds to. This way,
30+ when the field is changed, we know what the result type of the structure is
31+ going to be.
32+
33+ However, for a given type @t@, its representation @Rep t@ forgets which types
34+ in the structure came from type variables, and which didn't. An @Int@ that
35+ results from the instantiation of the type paremeter and an @Int@ that was
36+ monomorphically specified in the structure are indistinguishable.
37+
38+ The solution is to replace the type arguments in the type with unique
39+ proxies, like: @T a b@ -> @T (P 1 a) (P 0 b)@. This way, if looking up
40+ a field's type yields something of shape @P _ _@, we know it came from a type
41+ parameter, and also know which.
42+
43+ If the field's type is a proxy, then its type is allowed to change, otherwise
44+ not. This also allows us to satisfy the functional dependency @s field b -> t@.
45+ If after doing the conversion on @s@, @field@'s type is @(P _ a), then @t@ is
46+ @s[b/a]@, otherwise @t ~ s@ and @b ~ a@.
47+-}
48+
49+-- `P` can be used in place of any type parameter, which means that it can have
50+-- any kind, not just *, so a data type won't work.
51+-- (this caused https://github.com/kcsongor/generic-lens/issues/23)
52+-- Instead, we use a matchable type family to wrap any `k` - however, we can no longer directly
53+-- pattern match on `P`, as it's not a type constructor. But we can still take it apart as a polymorphic
54+-- application form. In order to distinguish between applications of P and other type constructors, we use a tag, `PTag`
55+-- to fake a type constructor.
56+data PTag = PTag
57+type family P :: Nat -> k -> PTag -> k
58+
59+type Indexed t = Indexed' t 0
60+
61+type family Indexed' (t :: k) (next :: Nat) :: k where
62+ Indexed' (t (a :: j) :: k) next = (Indexed' t (next + 1)) (P next a 'PTag)
63+ Indexed' t _ = t
64+
65+data Sub where
66+ Sub :: Nat -> k -> Sub
67+
68+type family Unify (a :: k) (b :: k) :: [Sub] where
69+ Unify (p n _ 'PTag) a' = '[ 'Sub n a']
70+ Unify (a x) (b y) = Unify x y ++ Unify a b
71+ Unify a a = '[]
72+ Unify a b = TypeError
73+ ( 'Text "Couldn't match type "
74+ ':<>: 'ShowType a
75+ ':<>: 'Text " with "
76+ ':<>: 'ShowType b
77+ )
78+
79+type family (xs :: [k]) ++ (ys :: [k]) :: [k] where
80+ '[] ++ ys = ys
81+ (x ': xs) ++ ys = x ': (xs ++ ys)
82+
83+type family Infer (s :: *) (a' :: *) (b :: *) :: * where
84+ Infer (s a) a' b
85+ = ReplaceArgs (s a) (Unify a' b)
86+ Infer s _ _ = s
87+
88+--------------------------------------------------------------------------------
89+
90+-- [TODO]: work this out
91+--
92+--type family ArgKind (t :: k) (pos :: Nat) :: * where
93+-- ArgKind (t (a :: k)) 'Z = k
94+-- ArgKind (t _) ('S pos) = ArgKind t pos
95+--
96+--type family ReplaceArg (t :: k) (pos :: Nat) (to :: ArgKind t pos) :: k where
97+-- ReplaceArg (t a) 'Z to = t to
98+-- ReplaceArg (t a) ('S pos) to = ReplaceArg t pos to a
99+-- ReplaceArg t _ _ = t
100+
101+type family ReplaceArg (t :: k) (pos :: Nat) (to :: j) :: k where
102+ ReplaceArg (t a) 0 to = t to
103+ ReplaceArg (t a) pos to = ReplaceArg t (pos - 1) to a
104+ ReplaceArg t _ _ = t
105+
106+type family ReplaceArgs (t :: k) (subs :: [Sub]) :: k where
107+ ReplaceArgs t '[] = t
108+ ReplaceArgs t ('Sub n arg ': ss) = ReplaceArgs (ReplaceArg t n arg) ss
109+
110+type family LookupParam (a :: k) (p :: Nat) :: Maybe Nat where
111+ LookupParam (param (n :: Nat)) m = 'Nothing
112+ LookupParam (a (_ (m :: Nat))) n = IfEq m n ('Just 0) (MaybeAdd (LookupParam a n) 1)
113+ LookupParam (a _) n = MaybeAdd (LookupParam a n) 1
114+ LookupParam a _ = 'Nothing
115+
116+type family MaybeAdd (a :: Maybe Nat) (b :: Nat) :: Maybe Nat where
117+ MaybeAdd 'Nothing _ = 'Nothing
118+ MaybeAdd ('Just a) b = 'Just (a + b)
119+
120+type family IfEq (a :: k) (b :: k) (t :: l) (f :: l) :: l where
121+ IfEq a a t _ = t
122+ IfEq _ _ _ f = f
123+
124+type family ArgCount (t :: k) :: Nat where
125+ ArgCount (f a) = 1 + ArgCount f
126+ ArgCount a = 0
127+
128+type family ArgAt (t :: k) (n :: Nat) :: j where
129+ ArgAt (t a) 0 = a
130+ ArgAt (t a) n = ArgAt t (n - 1)
131+
132+
133+-- Note [CPP in instance constraints]
134+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
135+--
136+-- In GHC 8.0.2, the calculated size of the expressions is too large for the
137+-- inliner to consider them under the default -funfolding-use-threshold value
138+-- (60).
139+--
140+-- To reduce the size, the constraints
141+--
142+-- ```
143+-- s' ~ Proxied s
144+-- t' ~ Proxied t
145+-- ```
146+--
147+-- are written as the following single equality:
148+--
149+-- ```
150+-- '(s', t') ~ '(Proxied s, Proxied t)
151+-- ```
152+--
153+-- However, for some reason, this violates the functional dependencies on 8.2.2.
154+-- Therefore, when using a newer version of the compiler, the original constraints
155+-- are used, as the expression size is smaller under 8.2.2.
156+
157+-- | Ensure that the types @a@ and @b@ are both applications of the same
158+-- constructor. The arguments may be different.
159+class UnifyHead (a :: k) (b :: k)
160+instance {-# OVERLAPPING #-} (gb ~ g b, UnifyHead f g) => UnifyHead (f a) gb
161+instance (a ~ b) => UnifyHead a b
src/Data/Generics/Internal/Families/Collect.hsnew file+133
@@ -0,0 +1,133 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE PolyKinds #-}
3+{-# LANGUAGE TypeFamilies #-}
4+{-# LANGUAGE TypeOperators #-}
5+{-# LANGUAGE UndecidableInstances #-}
6+
7+-----------------------------------------------------------------------------
8+-- |
9+-- Module : Data.Generics.Internal.Families.Collect
10+-- Copyright : (C) 2019 Csongor Kiss
11+-- License : BSD3
12+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
13+-- Stability : experimental
14+-- Portability : non-portable
15+--
16+-----------------------------------------------------------------------------
17+module Data.Generics.Internal.Families.Collect
18+ ( CollectTotalType
19+ , CollectPartialType
20+ , CollectField
21+ , CollectFieldsOrdered
22+ , TypeStat (..)
23+ , type (\\)
24+ ) where
25+
26+import Data.Type.Bool (If)
27+import Data.Type.Equality (type (==))
28+import GHC.Generics
29+import GHC.TypeLits (Symbol, CmpSymbol)
30+
31+import Data.Generics.Product.Internal.HList (type (++))
32+import Data.Generics.Internal.Families.Has (GTypes)
33+
34+data TypeStat
35+ = TypeStat
36+ { _containsNone :: [Symbol]
37+ , _containsMultiple :: [Symbol]
38+ , _containsOne :: [Symbol]
39+ }
40+
41+type EmptyStat = 'TypeStat '[] '[] '[]
42+
43+type family CollectTotalType t f :: TypeStat where
44+ CollectTotalType t (C1 ('MetaCons ctor _ _) f)
45+ = AddToStat ctor (CountType t f) EmptyStat
46+ CollectTotalType t (M1 _ _ r)
47+ = CollectTotalType t r
48+ CollectTotalType t (l :+: r)
49+ = MergeStat (CollectTotalType t l) (CollectTotalType t r)
50+
51+type family CollectField t f :: TypeStat where
52+ CollectField t (C1 ('MetaCons ctor _ _) f)
53+ = AddToStat ctor (CountField t f) EmptyStat
54+ CollectField t (M1 _ _ r)
55+ = CollectField t r
56+ CollectField t (l :+: r)
57+ = MergeStat (CollectField t l) (CollectField t r)
58+
59+type family AddToStat (ctor :: Symbol) (count :: Count) (st :: TypeStat) :: TypeStat where
60+ AddToStat ctor 'None ('TypeStat n m o) = 'TypeStat (ctor ': n) m o
61+ AddToStat ctor 'Multiple ('TypeStat n m o) = 'TypeStat n (ctor ': m) o
62+ AddToStat ctor 'One ('TypeStat n m o) = 'TypeStat n m (ctor ': o)
63+
64+type family MergeStat (st1 :: TypeStat) (st2 :: TypeStat) :: TypeStat where
65+ MergeStat ('TypeStat n m o) ('TypeStat n' m' o') = 'TypeStat (n ++ n') (m ++ m') (o ++ o')
66+
67+type family CountType t f :: Count where
68+ CountType t (S1 _ (Rec0 t))
69+ = 'One
70+ CountType t (l :*: r)
71+ = CountType t l <|> CountType t r
72+ CountType t _
73+ = 'None
74+
75+type family CountField (field :: Symbol) f :: Count where
76+ CountField field (S1 ('MetaSel ('Just field) _ _ _) _)
77+ = 'One
78+ CountField field (l :*: r)
79+ = CountField field l <|> CountField field r
80+ CountField _ _
81+ = 'None
82+
83+type family CollectPartialType t f :: [Symbol] where
84+ CollectPartialType t (l :+: r)
85+ = CollectPartialType t l ++ CollectPartialType t r
86+ CollectPartialType t (C1 ('MetaCons ctor _ _) f)
87+ = If (t == GTypes f) '[ctor] '[]
88+ CollectPartialType t (D1 _ f)
89+ = CollectPartialType t f
90+
91+data Count
92+ = None
93+ | One
94+ | Multiple
95+
96+type family (a :: Count) <|> (b :: Count) :: Count where
97+ 'None <|> b = b
98+ a <|> 'None = a
99+ a <|> b = 'Multiple
100+
101+type family (a :: Count) <&> (b :: Count) :: Count where
102+ a <&> a = a
103+ _ <&> _ = 'Multiple
104+
105+type family CollectFieldsOrdered (r :: * -> *) :: [Symbol] where
106+ CollectFieldsOrdered (l :*: r)
107+ = Merge (CollectFieldsOrdered l) (CollectFieldsOrdered r)
108+ CollectFieldsOrdered (S1 ('MetaSel ('Just name) _ _ _) _)
109+ = '[name]
110+ CollectFieldsOrdered (M1 _ m a)
111+ = CollectFieldsOrdered a
112+ CollectFieldsOrdered _
113+ = '[]
114+
115+type family Merge (xs :: [Symbol]) (ys :: [Symbol]) :: [Symbol] where
116+ Merge xs '[] = xs
117+ Merge '[] ys = ys
118+ Merge (x ': xs) (y ': ys) = Merge' (CmpSymbol x y) x y xs ys
119+
120+type family Merge' (ord :: Ordering) (x :: Symbol) (y :: Symbol) (xs :: [Symbol]) (ys :: [Symbol]) :: [Symbol] where
121+ Merge' 'LT x y xs ys = x ': Merge xs (y ': ys)
122+ Merge' _ x y xs ys = y ': Merge (x ': xs) ys
123+
124+type family (xs :: [Symbol]) \\ (ys :: [Symbol]) :: [Symbol] where
125+ xs \\ '[] = xs
126+ '[] \\ xs = '[]
127+ (x ': xs) \\ (y ': ys) = Sub' (CmpSymbol x y) x y xs ys
128+
129+infixr 5 \\
130+type family Sub' (ord :: Ordering) (x :: Symbol) (y :: Symbol) (xs :: [Symbol]) (ys :: [Symbol]) :: [Symbol] where
131+ Sub' 'LT x y xs ys = x ': (xs \\ y ': ys)
132+ Sub' 'GT x _ xs ys = (x ': xs) \\ ys
133+ Sub' 'EQ _ _ xs ys = xs \\ ys
src/Data/Generics/Internal/Families/Count.hsdeleted−75
@@ -1,75 +0,0 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE PolyKinds #-}
3−{-# LANGUAGE TypeFamilies #-}
4−{-# LANGUAGE TypeOperators #-}
5−{-# LANGUAGE UndecidableInstances #-}
6−
7−-----------------------------------------------------------------------------
8−-- |
9−-- Module : Data.Generics.Internal.Families.Count
10−-- Copyright : (C) 2017 Csongor Kiss
11−-- License : BSD3
12−-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
13−-- Stability : experimental
14−-- Portability : non-portable
15−--
16−-----------------------------------------------------------------------------
17−module Data.Generics.Internal.Families.Count
18− ( CountTotalType
19− , CountPartialType
20− , Count (..)
21− ) where
22−
23−import Data.Type.Bool (If)
24−import Data.Type.Equality (type (==))
25−import GHC.Generics
26−import GHC.TypeLits (TypeError, ErrorMessage (..))
27−
28−import Data.Generics.Internal.HList
29−
30−type family CountTotalType t f :: Count where
31− CountTotalType t (S1 _ (Rec0 t))
32− = 'One
33− CountTotalType t (l :*: r)
34− = CountTotalType t l <|> CountTotalType t r
35− CountTotalType t (l :+: r)
36− = CountTotalType t l <&> CountTotalType t r
37− CountTotalType t (S1 _ _)
38− = 'None
39− CountTotalType t (C1 _ f)
40− = CountTotalType t f
41− CountTotalType t (D1 _ f)
42− = CountTotalType t f
43− CountTotalType t (Rec0 _)
44− = 'None
45− CountTotalType t U1
46− = 'None
47− CountTotalType t V1
48− = 'None
49− CountTotalType t f
50− = TypeError
51− ( 'ShowType f
52− ':<>: 'Text " is not a valid GHC.Generics representation type"
53− )
54−
55−type family CountPartialType t f :: Count where
56− CountPartialType t (l :+: r)
57− = CountPartialType t l <|> CountPartialType t r
58− CountPartialType t (C1 m f)
59− = If (t == ListToTuple (GCollect f)) 'One 'None
60− CountPartialType t (D1 _ f)
61− = CountPartialType t f
62−
63−data Count
64− = None
65− | One
66− | Multiple
67−
68−type family (a :: Count) <|> (b :: Count) :: Count where
69− 'None <|> b = b
70− a <|> 'None = a
71− a <|> b = 'Multiple
72−
73−type family (a :: Count) <&> (b :: Count) :: Count where
74− a <&> a = a
75− _ <&> _ = 'Multiple
src/Data/Generics/Internal/Families/Has.hs+87−50
@@ -7,7 +7,7 @@
77-----------------------------------------------------------------------------
88-- |
99-- Module : Data.Generics.Internal.Families.Has
10−-- Copyright : (C) 2017 Csongor Kiss
10+-- Copyright : (C) 2019 Csongor Kiss
1111-- License : BSD3
1212-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1313-- Stability : experimental
@@ -17,73 +17,100 @@
1717module Data.Generics.Internal.Families.Has
1818 ( HasTotalFieldP
1919 , HasTotalTypeP
20− , HasPartialTypeTupleP
20+ , HasTotalPositionP
21+ , Pos
22+ , HasPartialTypeP
2123 , HasCtorP
24+ , GTypes
2225 ) where
2326
24−import Data.Type.Bool (type (||), type (&&))
27+import Data.Type.Bool (type (||))
2528import Data.Type.Equality (type (==))
2629import GHC.Generics
27−import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
30+import GHC.TypeLits (Symbol, Nat)
31+import Data.Kind (Type)
2832
29−import Data.Generics.Internal.HList
33+import Data.Generics.Product.Internal.HList
3034
31−type family HasTotalFieldP (field :: Symbol) f :: Bool where
32− HasTotalFieldP field (S1 ('MetaSel ('Just field) _ _ _) _)
33− = 'True
35+-- Note: these could be factored out into a single traversal
36+
37+type family Both (m1 :: Maybe a) (m2 :: Maybe a) :: Maybe a where
38+ Both ('Just a) ('Just a) = 'Just a
39+
40+type family Alt (m1 :: Maybe a) (m2 :: Maybe a) :: Maybe a where
41+ Alt ('Just a) _ = 'Just a
42+ Alt _ b = b
43+
44+type family HasTotalFieldP (field :: Symbol) f :: Maybe Type where
45+ HasTotalFieldP field (S1 ('MetaSel ('Just field) _ _ _) (Rec0 t))
46+ = 'Just t
3447 HasTotalFieldP field (l :*: r)
35− = HasTotalFieldP field l || HasTotalFieldP field r
48+ = Alt (HasTotalFieldP field l) (HasTotalFieldP field r)
3649 HasTotalFieldP field (l :+: r)
37− = HasTotalFieldP field l && HasTotalFieldP field r
50+ = Both (HasTotalFieldP field l) (HasTotalFieldP field r)
3851 HasTotalFieldP field (S1 _ _)
39− = 'False
52+ = 'Nothing
4053 HasTotalFieldP field (C1 _ f)
4154 = HasTotalFieldP field f
4255 HasTotalFieldP field (D1 _ f)
4356 = HasTotalFieldP field f
44− HasTotalFieldP field (Rec0 _)
45− = 'False
57+ HasTotalFieldP field (K1 _ _)
58+ = 'Nothing
4659 HasTotalFieldP field U1
47− = 'False
60+ = 'Nothing
4861 HasTotalFieldP field V1
49− = 'False
50− HasTotalFieldP field f
51− = TypeError
52− ( 'ShowType f
53− ':<>: 'Text " is not a valid GHC.Generics representation type"
54− )
62+ = 'Nothing
5563
56−type family HasTotalTypeP a f :: Bool where
57− HasTotalTypeP t (S1 meta (Rec0 t))
58− = 'True
59− HasTotalTypeP t (l :*: r)
60− = HasTotalTypeP t l || HasTotalTypeP t r
61− HasTotalTypeP t (l :+: r)
62− = HasTotalTypeP t l && HasTotalTypeP t r
63− HasTotalTypeP t (S1 _ _)
64− = 'False
65− HasTotalTypeP t (C1 m f)
66− = HasTotalTypeP t f
67− HasTotalTypeP t (D1 m f)
68− = HasTotalTypeP t f
69− HasTotalTypeP t (Rec0 _)
70− = 'False
71− HasTotalTypeP t U1
72− = 'False
73− HasTotalTypeP t V1
74− = 'False
75− HasTotalTypeP t f
76− = TypeError
77− ( 'ShowType f
78− ':<>: 'Text " is not a valid GHC.Generics representation type"
79− )
64+type family HasTotalTypeP (typ :: Type) f :: Maybe Type where
65+ HasTotalTypeP typ (S1 _ (K1 _ typ))
66+ = 'Just typ
67+ HasTotalTypeP typ (l :*: r)
68+ = Alt (HasTotalTypeP typ l) (HasTotalTypeP typ r)
69+ HasTotalTypeP typ (l :+: r)
70+ = Both (HasTotalTypeP typ l) (HasTotalTypeP typ r)
71+ HasTotalTypeP typ (S1 _ _)
72+ = 'Nothing
73+ HasTotalTypeP typ (C1 _ f)
74+ = HasTotalTypeP typ f
75+ HasTotalTypeP typ (D1 _ f)
76+ = HasTotalTypeP typ f
77+ HasTotalTypeP typ (K1 _ _)
78+ = 'Nothing
79+ HasTotalTypeP typ U1
80+ = 'Nothing
81+ HasTotalTypeP typ V1
82+ = 'Nothing
8083
81−type family HasPartialTypeTupleP a f :: Bool where
82− HasPartialTypeTupleP t (l :+: r)
83− = HasPartialTypeTupleP t l || HasPartialTypeTupleP t r
84− HasPartialTypeTupleP t (C1 m f)
85− = t == ListToTuple (GCollect f)
86− HasPartialTypeTupleP t _
84+data Pos (p :: Nat)
85+
86+type family HasTotalPositionP (pos :: Nat) f :: Maybe Type where
87+ HasTotalPositionP pos (S1 _ (K1 (Pos pos) t))
88+ = 'Just t
89+ HasTotalPositionP pos (l :*: r)
90+ = Alt (HasTotalPositionP pos l) (HasTotalPositionP pos r)
91+ HasTotalPositionP pos (l :+: r)
92+ = Both (HasTotalPositionP pos l) (HasTotalPositionP pos r)
93+ HasTotalPositionP pos (S1 _ _)
94+ = 'Nothing
95+ HasTotalPositionP pos (C1 _ f)
96+ = HasTotalPositionP pos f
97+ HasTotalPositionP pos (D1 _ f)
98+ = HasTotalPositionP pos f
99+ HasTotalPositionP pos (K1 _ _)
100+ = 'Nothing
101+ HasTotalPositionP pos U1
102+ = 'Nothing
103+ HasTotalPositionP pos V1
104+ = 'Nothing
105+
106+type family HasPartialTypeP a f :: Bool where
107+ HasPartialTypeP t (l :+: r)
108+ = HasPartialTypeP t l || HasPartialTypeP t r
109+ HasPartialTypeP t (C1 m f)
110+ = t == GTypes f
111+ HasPartialTypeP t (M1 _ _ f)
112+ = HasPartialTypeP t f
113+ HasPartialTypeP t _
87114 = 'False
88115
89116type family HasCtorP (ctor :: Symbol) f :: Bool where
@@ -95,3 +122,13 @@ type family HasCtorP (ctor :: Symbol) f :: Bool where
95122 = HasCtorP ctor f
96123 HasCtorP ctor _
97124 = 'False
125+
126+type family GTypes (rep :: Type -> Type) :: [Type] where
127+ GTypes (l :*: r)
128+ = GTypes l ++ GTypes r
129+ GTypes (K1 _ a)
130+ = '[ a]
131+ GTypes (M1 _ m a)
132+ = GTypes a
133+ GTypes U1 = '[]
134+
src/Data/Generics/Internal/GenericN.hsnew file+75
@@ -0,0 +1,75 @@
1+{-# OPTIONS_HADDOCK hide #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE InstanceSigs #-}
6+{-# LANGUAGE PolyKinds #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeFamilies #-}
9+{-# LANGUAGE TypeOperators #-}
10+{-# LANGUAGE UndecidableInstances #-}
11+
12+--------------------------------------------------------------------------------
13+-- |
14+-- Module : Data.Generics.Internal.GenericN
15+-- Copyright : (C) 2019 Csongor Kiss
16+-- License : BSD3
17+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
18+-- Stability : experimental
19+-- Portability : non-portable
20+--
21+-- Generic representation of types with multiple parameters
22+--
23+--------------------------------------------------------------------------------
24+
25+module Data.Generics.Internal.GenericN
26+ ( Param
27+ , Rec (Rec, unRec)
28+ , GenericN (..)
29+ ) where
30+
31+import Data.Kind
32+import GHC.Generics
33+import GHC.TypeLits
34+import Data.Coerce
35+
36+type family Param :: Nat -> k where
37+
38+type family Indexed (t :: k) (i :: Nat) :: k where
39+ Indexed (t a) i = Indexed t (i + 1) (Param i)
40+ Indexed t _ = t
41+
42+newtype Rec (p :: Type) a x = Rec { unRec :: K1 R a x }
43+
44+type family Zip (a :: Type -> Type) (b :: Type -> Type) :: Type -> Type where
45+ Zip (M1 mt m s) (M1 mt m t)
46+ = M1 mt m (Zip s t)
47+ Zip (l :+: r) (l' :+: r')
48+ = Zip l l' :+: Zip r r'
49+ Zip (l :*: r) (l' :*: r')
50+ = Zip l l' :*: Zip r r'
51+ Zip (Rec0 p) (Rec0 a)
52+ = Rec p a
53+ Zip U1 U1
54+ = U1
55+
56+class
57+ ( Coercible (Rep a) (RepN a)
58+ , Generic a
59+ ) => GenericN (a :: Type) where
60+ type family RepN (a :: Type) :: Type -> Type
61+ type instance RepN a = Zip (Rep (Indexed a 0)) (Rep a)
62+ toN :: RepN a x -> a
63+ fromN :: a -> RepN a x
64+
65+instance
66+ ( Coercible (Rep a) (RepN a)
67+ , Generic a
68+ ) => GenericN a where
69+ toN :: forall x. RepN a x -> a
70+ toN = coerce (to :: Rep a x -> a)
71+ {-# INLINE toN #-}
72+
73+ fromN :: forall x. a -> RepN a x
74+ fromN = coerce (from :: a -> Rep a x)
75+ {-# INLINE fromN #-}
src/Data/Generics/Internal/Lens.hsdeleted−109
@@ -1,109 +0,0 @@
1−{-# LANGUAGE GADTs #-}
2−{-# LANGUAGE Rank2Types #-}
3−{-# LANGUAGE TypeOperators #-}
4−
5−-----------------------------------------------------------------------------
6−-- |
7−-- Module : Data.Generics.Internal.Lens
8−-- Copyright : (C) 2017 Csongor Kiss
9−-- License : BSD3
10−-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
11−-- Stability : experimental
12−-- Portability : non-portable
13−--
14−-- Internal lens helpers. Only exported for Haddock
15−--
16−-----------------------------------------------------------------------------
17−module Data.Generics.Internal.Lens where
18−
19−import Control.Applicative (Const(..))
20−import Data.Functor.Identity (Identity(..))
21−import Data.Profunctor (Choice(right'), Profunctor(dimap))
22−import GHC.Generics ((:*:)(..), (:+:)(..), Generic(..), M1(..), Rep)
23−
24−-- | Type alias for lens
25−type Lens' s a
26− = forall f. Functor f => (a -> f a) -> s -> f s
27−
28−-- | Type alias for prism
29−type Prism' s a
30− = forall p f. (Choice p, Applicative f) => p a (f a) -> p s (f s)
31−
32−type Iso' s a
33− = forall p f. (Profunctor p, Functor f) => p a (f a) -> p s (f s)
34−
35−-- | Getting
36−(^.) :: s -> ((a -> Const a a) -> s -> Const a s) -> a
37−s ^. l = getConst (l Const s)
38−infixl 8 ^.
39−
40−-- | Setting
41−set :: ((a -> Identity b) -> s -> Identity t) -> b -> s -> t
42−set l b
43− = runIdentity . l (\_ -> Identity b)
44−
45−-- | Lens focusing on the first element of a product
46−first :: Lens' ((a :*: b) x) (a x)
47−first f (a :*: b)
48− = fmap (:*: b) (f a)
49−
50−-- | Lens focusing on the second element of a product
51−second :: Lens' ((a :*: b) x) (b x)
52−second f (a :*: b)
53− = fmap (a :*:) (f b)
54−
55−left :: Prism' ((a :+: b) x) (a x)
56−left = prism L1 $ \x -> case x of
57− L1 a -> Right a
58− R1 _ -> Left x
59−
60−right :: Prism' ((a :+: b) x) (b x)
61−right = prism R1 $ \x -> case x of
62− L1 _ -> Left x
63− R1 a -> Right a
64−
65−combine :: Lens' (s x) a -> Lens' (t x) a -> Lens' ((s :+: t) x) a
66−combine sa _ f (L1 s) = fmap (\a -> L1 (set sa a s)) (f (s ^. sa))
67−combine _ ta f (R1 t) = fmap (\a -> R1 (set ta a t)) (f (t ^. ta))
68−
69−prism :: (a -> s) -> (s -> Either s a) -> Prism' s a
70−prism bt seta = dimap seta (either pure (fmap bt)) . right'
71−
72−-- | A type and its generic representation are isomorphic
73−repIso :: Generic a => Iso' a (Rep a x)
74−repIso = dimap from (fmap to)
75−
76−-- | 'M1' is just a wrapper around `f p`
77−mIso :: Iso' (M1 i c f p) (f p)
78−mIso = dimap unM1 (fmap M1)
79−
80−
81−sumIso :: Iso' ((a :+: b) x) (Either (a x) (b x))
82−sumIso = dimap f (fmap t)
83− where f (L1 x) = Left x
84− f (R1 x) = Right x
85− t (Left x) = L1 x
86− t (Right x) = R1 x
87−
88−_Left :: Prism' (Either a c) a
89−_Left = prism Left $ either Right (Left . Right)
90−
91−_Right :: Prism' (Either c a) a
92−_Right = prism Right $ either (Left . Left) Right
93−
94−--------------------------------------------------------------------------------
95−
96−data Coyoneda f b = forall a. Coyoneda (a -> b) (f a)
97−
98−instance Functor (Coyoneda f) where
99− fmap f (Coyoneda g fa)
100− = Coyoneda (f . g) fa
101−
102−inj :: Functor f => Coyoneda f a -> f a
103−inj (Coyoneda f a) = fmap f a
104−
105−proj :: Functor f => f a -> Coyoneda f a
106−proj fa = Coyoneda id fa
107−
108−ravel :: Functor f => ((a -> Coyoneda f b) -> s -> Coyoneda f t) -> (a -> f b) -> (s -> f t)
109−ravel coy f s = inj $ coy (\a -> proj (f a)) s
src/Data/Generics/Internal/Profunctor/Iso.hsnew file+111
@@ -0,0 +1,111 @@
1+{-# OPTIONS_HADDOCK hide #-}
2+{-# LANGUAGE TupleSections #-}
3+{-# LANGUAGE GADTs #-}
4+{-# LANGUAGE NoMonomorphismRestriction #-}
5+{-# LANGUAGE Rank2Types #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE TypeFamilyDependencies #-}
9+{-# LANGUAGE TypeOperators #-}
10+
11+-----------------------------------------------------------------------------
12+-- |
13+-- Module : Data.Generics.Internal.Profunctor.Iso
14+-- Copyright : (C) 2019 Csongor Kiss
15+-- License : BSD3
16+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17+-- Stability : experimental
18+-- Portability : non-portable
19+--
20+-- Internal lens helpers. Only exported for Haddock
21+--
22+-----------------------------------------------------------------------------
23+module Data.Generics.Internal.Profunctor.Iso where
24+
25+import Data.Profunctor (Profunctor(..))
26+import Data.Profunctor.Unsafe ((#.), (.#))
27+import GHC.Generics ((:*:)(..), (:+:)(..), Generic(..), M1(..), K1(..), Rep)
28+import Data.Coerce
29+import Data.Generics.Internal.GenericN (Rec (..))
30+
31+import qualified Data.Generics.Internal.VL.Iso as VL
32+
33+type Iso s t a b
34+ = forall p. (Profunctor p) => p a b -> p s t
35+
36+type Iso' s a = Iso s s a a
37+
38+-- | A type and its generic representation are isomorphic
39+repIso :: (Generic a, Generic b) => Iso a b (Rep a x) (Rep b x)
40+repIso = iso from to
41+
42+-- | 'M1' is just a wrapper around `f p`
43+--mIso :: Iso' (M1 i c f p) (f p)
44+mIso :: Iso (M1 i c f p) (M1 i c g p) (f p) (g p)
45+mIso = iso unM1 M1
46+{-# INLINE mIso #-}
47+
48+kIso :: Iso (K1 r a p) (K1 r b p) a b
49+kIso = iso unK1 K1
50+{-# INLINE kIso #-}
51+
52+recIso :: Iso (Rec r a p) (Rec r b p) a b
53+recIso = iso (unK1 . unRec) (Rec . K1)
54+{-# INLINE recIso #-}
55+
56+sumIso :: Iso ((a :+: b) x) ((a' :+: b') x) (Either (a x) (b x)) (Either (a' x) (b' x))
57+sumIso = iso back forth
58+ where forth (Left l) = L1 l
59+ forth (Right r) = R1 r
60+ back (L1 l) = Left l
61+ back (R1 r) = Right r
62+{-# INLINE sumIso #-}
63+
64+prodIso :: Iso ((a :*: b) x) ((a' :*: b') x) (a x, b x) (a' x, b' x)
65+prodIso = iso (\(a :*: b) -> (a, b)) (\(a, b) -> (a :*: b))
66+
67+assoc3 :: Iso ((a, b), c) ((a', b'), c') (a, (b, c)) (a', (b', c'))
68+assoc3 = iso (\((a, b), c) -> (a, (b, c))) (\(a, (b, c)) -> ((a, b), c))
69+
70+--------------------------------------------------------------------------------
71+-- Iso stuff
72+
73+fromIso :: Iso s t a b -> Iso b a t s
74+fromIso l = withIso l $ \ sa bt -> iso bt sa
75+{-# INLINE fromIso #-}
76+
77+iso :: (s -> a) -> (b -> t) -> Iso s t a b
78+iso = dimap
79+{-# INLINE iso #-}
80+
81+iso2isovl :: Iso s t a b -> VL.Iso s t a b
82+iso2isovl _iso = withIso _iso VL.iso
83+{-# INLINE iso2isovl #-}
84+
85+withIso :: Iso s t a b -> ((s -> a) -> (b -> t) -> r) -> r
86+withIso ai k = case ai (Exchange id id) of
87+ Exchange sa bt -> k sa bt
88+
89+pairing :: Iso s t a b -> Iso s' t' a' b' -> Iso (s, s') (t, t') (a, a') (b, b')
90+pairing f g = withIso f $ \ sa bt -> withIso g $ \s'a' b't' ->
91+ iso (bmap sa s'a') (bmap bt b't')
92+ where bmap f' g' (a, b) = (f' a, g' b)
93+
94+data Exchange a b s t = Exchange (s -> a) (b -> t)
95+
96+instance Functor (Exchange a b s) where
97+ fmap f (Exchange sa bt) = Exchange sa (f . bt)
98+ {-# INLINE fmap #-}
99+
100+instance Profunctor (Exchange a b) where
101+ dimap f g (Exchange sa bt) = Exchange (sa . f) (g . bt)
102+ {-# INLINE dimap #-}
103+ lmap f (Exchange sa bt) = Exchange (sa . f) bt
104+ {-# INLINE lmap #-}
105+ rmap f (Exchange sa bt) = Exchange sa (f . bt)
106+ {-# INLINE rmap #-}
107+ ( #. ) _ = coerce
108+ {-# INLINE ( #. ) #-}
109+ ( .# ) p _ = coerce p
110+ {-# INLINE ( .# ) #-}
111+
src/Data/Generics/Internal/Profunctor/Lens.hsnew file+171
@@ -0,0 +1,171 @@
1+{-# OPTIONS_HADDOCK hide #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE NoMonomorphismRestriction #-}
4+{-# LANGUAGE Rank2Types #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TupleSections #-}
7+{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE TypeFamilyDependencies #-}
9+{-# LANGUAGE TypeOperators #-}
10+
11+-----------------------------------------------------------------------------
12+-- |
13+-- Module : Data.Generics.Internal.Profunctor.Lens
14+-- Copyright : (C) 2019 Csongor Kiss
15+-- License : BSD3
16+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17+-- Stability : experimental
18+-- Portability : non-portable
19+--
20+-- Internal lens helpers. Only exported for Haddock
21+--
22+-----------------------------------------------------------------------------
23+module Data.Generics.Internal.Profunctor.Lens where
24+
25+import Data.Profunctor (Profunctor(..), Strong(..))
26+import Data.Bifunctor
27+import GHC.Generics
28+import Data.Generics.Internal.Profunctor.Iso
29+
30+type Lens s t a b
31+ = forall p . (Strong p) => p a b -> p s t
32+
33+type LensLike p s t a b
34+ = p a b -> p s t
35+
36+
37+ravel :: (ALens a b a b -> ALens a b s t) -> Lens s t a b
38+ravel l pab = conv (l idLens) pab
39+ where
40+ conv :: ALens a b s t -> Lens s t a b
41+ conv (ALens _get _set) = lens _get _set
42+
43+-- | Setting
44+set :: ((a -> b) -> s -> t) -> (s, b) -> t
45+set f (s, b)
46+ = f (const b) s
47+
48+view :: Lens s s a a -> s -> a
49+view l = withLensPrim l (\get _ -> snd . get)
50+
51+--withLens :: Lens s t a b -> ((s -> a) -> ((s, b) -> t) -> r) -> r
52+--ithLens l k =
53+-- case l idLens of
54+-- ALens _get _set -> k (snd . _get) (\(s, b) -> _set ((fst $ _get s), b))
55+
56+withLensPrim :: Lens s t a b -> (forall c . (s -> (c,a)) -> ((c, b) -> t) -> r) -> r
57+withLensPrim l k =
58+ case l idLens of
59+ ALens _get _set -> k _get _set
60+
61+idLens :: ALens a b a b
62+idLens = ALens (fork (const ()) id) snd
63+{-# INLINE idLens #-}
64+
65+-- | Lens focusing on the first element of a product
66+first :: Lens ((a :*: b) x) ((a' :*: b) x) (a x) (a' x)
67+first
68+ = lens (\(a :*: b) -> (b,a)) (\(b, a') -> a' :*: b)
69+
70+-- | Lens focusing on the second element of a product
71+second :: Lens ((a :*: b) x) ((a :*: b') x) (b x) (b' x)
72+second
73+ = lens (\(a :*: b) -> (a,b)) (\(a, b') -> a :*: b')
74+
75+fork :: (a -> b) -> (a -> c) -> a -> (b, c)
76+fork f g a = (f a, g a)
77+
78+swap :: (a, b) -> (b, a)
79+swap (a, b) = (b, a)
80+
81+cross :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)
82+cross = bimap
83+
84+--------------------------------------------------------------------------------
85+
86+data Coyoneda f b = forall a. Coyoneda (a -> b) (f a)
87+
88+instance Functor (Coyoneda f) where
89+ fmap f (Coyoneda g fa)
90+ = Coyoneda (f . g) fa
91+
92+inj :: Functor f => Coyoneda f a -> f a
93+inj (Coyoneda f a) = fmap f a
94+
95+proj :: Functor f => f a -> Coyoneda f a
96+proj fa = Coyoneda id fa
97+
98+newtype Alongside p s t a b = Alongside { getAlongside :: p (s, a) (t, b) }
99+
100+instance Profunctor p => Profunctor (Alongside p c d) where
101+ dimap f g (Alongside pab) = Alongside $ dimap (fmap f) (fmap g) pab
102+
103+instance Strong p => Strong (Alongside p c d) where
104+ second' (Alongside pab) = Alongside . dimap shuffle shuffle . second' $ pab
105+ where
106+ shuffle (x,(y,z)) = (y,(x,z))
107+
108+(??) :: Functor f => f (a -> b) -> a -> f b
109+fab ?? a = fmap ($ a) fab
110+
111+-- Could implement this using primitives?
112+alongside :: Profunctor p =>
113+ LensLike (Alongside p s' t') s t a b
114+ -> LensLike (Alongside p a b) s' t' a' b'
115+ -> LensLike p (s, s') (t, t') (a, a') (b, b')
116+alongside l1 l2
117+ = dimap swap swap . getAlongside . l1 . Alongside . dimap swap swap . getAlongside . l2 . Alongside
118+
119+assoc3L :: Lens ((a, b), c) ((a', b'), c') (a, (b, c)) (a', (b', c'))
120+assoc3L f = assoc3 f
121+
122+stron :: (Either s s', b) -> Either (s, b) (s', b)
123+stron (e, b) = bimap (,b) (, b) e
124+
125+choosing :: forall s t a b s' t' . Lens s t a b -> Lens s' t' a b -> Lens (Either s s') (Either t t') a b
126+choosing l r = withLensPrim l (\getl setl ->
127+ withLensPrim r (\getr setr ->
128+ let --g :: Either s s' -> a
129+ g e = case e of
130+ Left v -> let (c, v') = getl v in (Left c, v')
131+ Right v -> let (c, v') = getr v in (Right c, v')
132+ s = bimap setl setr . stron
133+ in lens g s))
134+
135+lens :: (s -> (c,a)) -> ((c,b) -> t) -> Lens s t a b
136+lens get _set = dimap get _set . second'
137+{-# INLINE lens #-}
138+
139+------------------------------------------------------------------------------
140+
141+data ALens a b s t = forall c . ALens (s -> (c,a)) ((c, b) -> t)
142+
143+instance Functor (ALens a b s) where
144+ fmap f (ALens _get _set) = ALens _get (f . _set)
145+
146+instance Profunctor (ALens a b) where
147+ dimap f g (ALens get _set) = ALens (get . f) (g . _set)
148+
149+instance Strong (ALens a b) where
150+ second' (ALens get _set) = ALens get' set' --(bimap id _set . assoc)
151+ where
152+ get' (c, a1) = let (c1, a) = get a1 in ((c, c1), a)
153+ set' ((c, c1), b) = (c, _set (c1, b))
154+ {-# INLINE second' #-}
155+
156+-- These are specialised versions of the Isos. On GHC 8.0.2, having
157+-- these functions eta-expanded allows the optimiser to inline these functions.
158+mLens :: Lens (M1 i c f p) (M1 i c g p) (f p) (g p)
159+mLens f = mIso f
160+
161+repLens :: (Generic a, Generic b) => Lens a b (Rep a x) (Rep b x)
162+repLens f = repIso f
163+
164+prodL :: Lens ((a :*: b) x) ((a' :*: b') x) (a x, b x) (a' x, b' x)
165+prodL f = prodIso f
166+
167+prodR :: Lens (a' x, b' x) (a x, b x) ((a' :*: b') x) ((a :*: b) x)
168+prodR f = fromIso prodIso f
169+
170+assoc3R :: Lens (a', (b', c')) (a, (b, c)) ((a', b'), c') ((a, b), c)
171+assoc3R f = fromIso assoc3 f
src/Data/Generics/Internal/Profunctor/Prism.hsnew file+117
@@ -0,0 +1,117 @@
1+{-# OPTIONS_HADDOCK hide #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE NoMonomorphismRestriction #-}
5+{-# LANGUAGE Rank2Types #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE TypeFamilyDependencies #-}
9+{-# LANGUAGE TypeOperators #-}
10+
11+-----------------------------------------------------------------------------
12+-- |
13+-- Module : Data.Generics.Internal.Profunctor.Prism
14+-- Copyright : (C) 2019 Csongor Kiss
15+-- License : BSD3
16+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17+-- Stability : experimental
18+-- Portability : non-portable
19+--
20+-- Internal lens helpers. Only exported for Haddock
21+--
22+-----------------------------------------------------------------------------
23+module Data.Generics.Internal.Profunctor.Prism where
24+
25+import Data.Bifunctor (bimap)
26+import Data.Profunctor (Choice(..), Profunctor(..))
27+import Data.Tagged
28+import Data.Profunctor.Unsafe ((#.), (.#))
29+import GHC.Generics
30+import Data.Coerce
31+
32+type APrism s t a b = Market a b a b -> Market a b s t
33+
34+type Prism s t a b
35+ = forall p . (Choice p) => p a b -> p s t
36+
37+type Prism' s a = forall p . (Choice p) => p a a -> p s s
38+
39+left :: Prism ((a :+: c) x) ((b :+: c) x) (a x) (b x)
40+left = prism L1 $ gsum Right (Left . R1)
41+
42+right :: Prism ((a :+: b) x) ((a :+: c) x) (b x) (c x)
43+right = prism R1 $ gsum (Left . L1) Right
44+
45+prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
46+prism bt seta eta = dimap seta (either id bt) (right' eta)
47+
48+_Left :: Prism (Either a c) (Either b c) a b
49+_Left = left'
50+
51+_Right :: Prism (Either c a) (Either c b) a b
52+_Right = right'
53+
54+prismPRavel :: APrism s t a b -> Prism s t a b
55+prismPRavel l pab = (prism2prismp $ l idPrism) pab
56+
57+build :: (Tagged b b -> Tagged t t) -> b -> t
58+build p = unTagged #. p .# Tagged
59+
60+match :: Prism s t a b -> s -> Either t a
61+match k = withPrism k $ \_ _match -> _match
62+
63+--------------------------------------------------------------------------------
64+-- Prism stuff
65+
66+without' :: Prism s t a b -> Prism s t c d -> Prism s t (Either a c) (Either b d)
67+without' k =
68+ withPrism k $ \bt _ k' ->
69+ withPrism k' $ \dt setc ->
70+ prism (either bt dt) $ \s -> fmap Right (setc s)
71+{-# INLINE without' #-}
72+
73+withPrism :: APrism s t a b -> ((b -> t) -> (s -> Either t a) -> r) -> r
74+withPrism k f = case k idPrism of
75+ Market bt seta -> f bt seta
76+
77+prism2prismp :: Market a b s t -> Prism s t a b
78+prism2prismp (Market bt seta) = prism bt seta
79+
80+idPrism :: Market a b a b
81+idPrism = Market id Right
82+
83+gsum :: (a x -> c) -> (b x -> c) -> ((a :+: b) x) -> c
84+gsum f _ (L1 x) = f x
85+gsum _ g (R1 y) = g y
86+
87+plus :: (a -> b) -> (c -> d) -> Either a c -> Either b d
88+plus = bimap
89+
90+--------------------------------------------------------------------------------
91+-- Market
92+
93+data Market a b s t = Market (b -> t) (s -> Either t a)
94+
95+instance Functor (Market a b s) where
96+ fmap f (Market bt seta) = Market (f . bt) (either (Left . f) Right . seta)
97+ {-# INLINE fmap #-}
98+
99+instance Profunctor (Market a b) where
100+ dimap f g (Market bt seta) = Market (g . bt) (either (Left . g) Right . seta . f)
101+ {-# INLINE dimap #-}
102+ lmap f (Market bt seta) = Market bt (seta . f)
103+ {-# INLINE lmap #-}
104+ rmap f (Market bt seta) = Market (f . bt) (either (Left . f) Right . seta)
105+ {-# INLINE rmap #-}
106+ ( #. ) _ = coerce
107+ {-# INLINE ( #. ) #-}
108+ ( .# ) p _ = coerce p
109+ {-# INLINE ( .# ) #-}
110+
111+instance Choice (Market a b) where
112+ left' (Market bt seta) = Market (Left . bt) $ \case
113+ Left s -> case seta s of
114+ Left t -> Left (Left t)
115+ Right a -> Right a
116+ Right c -> Left (Right c)
117+ {-# INLINE left' #-}
src/Data/Generics/Internal/VL/Iso.hsnew file+80
@@ -0,0 +1,80 @@
1+{-# OPTIONS_HADDOCK hide #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE NoMonomorphismRestriction #-}
4+{-# LANGUAGE Rank2Types #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE TypeFamilyDependencies #-}
8+{-# LANGUAGE TypeOperators #-}
9+
10+-----------------------------------------------------------------------------
11+-- |
12+-- Module : Data.Generics.Internal.VL.Iso
13+-- Copyright : (C) 2019 Csongor Kiss
14+-- License : BSD3
15+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
16+-- Stability : experimental
17+-- Portability : non-portable
18+--
19+-- Internal lens helpers. Only exported for Haddock
20+--
21+-----------------------------------------------------------------------------
22+module Data.Generics.Internal.VL.Iso where
23+
24+import Data.Coerce (coerce)
25+import Data.Functor.Identity (Identity(..))
26+import Data.Profunctor (Profunctor(..))
27+import GHC.Generics
28+import Data.Generics.Internal.GenericN (Rec (..))
29+
30+data Exchange a b s t = Exchange (s -> a) (b -> t)
31+
32+instance Functor (Exchange a b s) where
33+ fmap f (Exchange sa bt) = Exchange sa (f . bt)
34+ {-# INLINE fmap #-}
35+
36+instance Profunctor (Exchange a b) where
37+ dimap f g (Exchange sa bt) = Exchange (sa . f) (g . bt)
38+ {-# INLINE dimap #-}
39+ lmap f (Exchange sa bt) = Exchange (sa . f) bt
40+ {-# INLINE lmap #-}
41+ rmap f (Exchange sa bt) = Exchange sa (f . bt)
42+ {-# INLINE rmap #-}
43+
44+type Iso' s a
45+ = forall p f. (Profunctor p, Functor f) => p a (f a) -> p s (f s)
46+
47+type Iso s t a b
48+ = forall p f. (Profunctor p, Functor f) => p a (f b) -> p s (f t)
49+
50+fromIso :: Iso s t a b -> Iso b a t s
51+fromIso l = withIso l $ \ sa bt -> iso bt sa
52+{-# inline fromIso #-}
53+
54+-- | Extract the two functions, one from @s -> a@ and
55+-- one from @b -> t@ that characterize an 'Iso'.
56+withIso :: Iso s t a b -> ((s -> a) -> (b -> t) -> r) -> r
57+withIso ai k = case ai (Exchange id Identity) of
58+ Exchange sa bt -> k sa (coerce bt)
59+{-# inline withIso #-}
60+
61+-- | A type and its generic representation are isomorphic
62+repIso :: (Generic a, Generic b) => Iso a b (Rep a x) (Rep b x)
63+repIso = iso from to
64+
65+-- | 'M1' is just a wrapper around `f p`
66+mIso :: Iso (M1 i c f p) (M1 i c g p) (f p) (g p)
67+mIso = iso unM1 M1
68+
69+kIso :: Iso (K1 r a p) (K1 r b p) a b
70+kIso = iso unK1 K1
71+
72+recIso :: Iso (Rec r a p) (Rec r b p) a b
73+recIso = iso (unK1 . unRec) (Rec . K1)
74+
75+prodIso :: Iso ((a :*: b) x) ((a' :*: b') x) (a x, b x) (a' x, b' x)
76+prodIso = iso (\(a :*: b) -> (a, b)) (\(a, b) -> (a :*: b))
77+
78+iso :: (s -> a) -> (b -> t) -> Iso s t a b
79+iso sa bt = dimap sa (fmap bt)
80+{-# INLINE iso #-}
src/Data/Generics/Internal/VL/Lens.hsnew file+70
@@ -0,0 +1,70 @@
1+{-# OPTIONS_HADDOCK hide #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE NoMonomorphismRestriction #-}
4+{-# LANGUAGE Rank2Types #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE TypeFamilyDependencies #-}
8+{-# LANGUAGE TypeOperators #-}
9+{-# LANGUAGE TupleSections #-}
10+
11+-----------------------------------------------------------------------------
12+-- |
13+-- Module : Data.Generics.Internal.VL.Lens
14+-- Copyright : (C) 2019 Csongor Kiss
15+-- License : BSD3
16+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17+-- Stability : experimental
18+-- Portability : non-portable
19+--
20+-- Internal lens helpers. Only exported for Haddock
21+--
22+-----------------------------------------------------------------------------
23+module Data.Generics.Internal.VL.Lens where
24+
25+import Control.Applicative (Const(..))
26+import Data.Coerce (coerce)
27+import Data.Functor.Identity (Identity(..))
28+import Data.Generics.Internal.Profunctor.Lens (ALens (..), idLens)
29+
30+-- | Type alias for lens
31+type Lens' s a
32+ = Lens s s a a
33+
34+type Lens s t a b
35+ = forall f. Functor f => (a -> f b) -> s -> f t
36+
37+view :: ((a -> Const a a) -> s -> Const a s) -> s -> a
38+view l s = (^.) s l
39+
40+-- | Getting
41+(^.) :: s -> ((a -> Const a a) -> s -> Const a s) -> a
42+s ^. l = getConst (l Const s)
43+infixl 8 ^.
44+
45+infixr 4 .~
46+(.~) :: ((a -> Identity b) -> s -> Identity t) -> b -> s -> t
47+(.~) f b = runIdentity . f (Identity . const b)
48+
49+set :: Lens s t a b -> b -> s -> t
50+set l x = l .~ x
51+
52+over :: ((a -> Identity b) -> s -> Identity t) -> (a -> b) -> s -> t
53+over = coerce
54+
55+lens2lensvl :: ALens a b s t -> Lens s t a b
56+lens2lensvl (ALens _get _set) =
57+ \f x ->
58+ case _get x of
59+ (c, a) -> _set . (c, ) <$> f a
60+{-# INLINE lens2lensvl #-}
61+
62+ravel :: (ALens a b a b -> ALens a b s t)
63+ -> Lens s t a b
64+ravel l pab = (lens2lensvl $ l idLens) pab
65+
66+
67+lens :: (s -> a) -> ((s, b) -> t) -> Lens s t a b
68+lens get _set = \f x -> curry _set x <$> f (get x)
69+{-# INLINE[0] lens #-}
70+
src/Data/Generics/Internal/VL/Prism.hsnew file+74
@@ -0,0 +1,74 @@
1+{-# OPTIONS_HADDOCK hide #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE NoMonomorphismRestriction #-}
4+{-# LANGUAGE Rank2Types #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE TypeFamilyDependencies #-}
8+{-# LANGUAGE TypeOperators #-}
9+
10+-----------------------------------------------------------------------------
11+-- |
12+-- Module : Data.Generics.Internal.VL.Prism
13+-- Copyright : (C) 2019 Csongor Kiss
14+-- License : BSD3
15+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
16+-- Stability : experimental
17+-- Portability : non-portable
18+--
19+-- Internal lens helpers. Only exported for Haddock
20+--
21+-----------------------------------------------------------------------------
22+module Data.Generics.Internal.VL.Prism where
23+
24+import Data.Functor.Identity (Identity(..))
25+import Data.Profunctor (Choice(..), Profunctor(..))
26+import Data.Coerce
27+import Data.Generics.Internal.Profunctor.Prism (Market (..), plus, idPrism)
28+import Data.Tagged
29+import Data.Profunctor.Unsafe ((#.), (.#))
30+import Data.Monoid (First (..))
31+import Control.Applicative (Const(..))
32+
33+-- | Type alias for prism
34+type Prism s t a b
35+ = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)
36+
37+type Prism' s a
38+ = Prism s s a a
39+
40+infixl 8 ^?
41+(^?) :: s -> ((a -> Const (First a) a) -> s -> Const (First a) s) -> Maybe a
42+s ^? l = getFirst (fmof l (First #. Just) s)
43+ where fmof l' f = getConst #. l' (Const #. f)
44+
45+
46+match :: Prism s t a b -> s -> Either t a
47+match k = withPrism k $ \_ _match -> _match
48+{-# INLINE match #-}
49+
50+(#) :: (Tagged b (Identity b) -> Tagged t (Identity t)) -> b -> t
51+(#) = build
52+{-# INLINE (#) #-}
53+
54+prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
55+prism bt seta eta = dimap (\x -> plus pure id (seta x)) (either id (\x -> fmap bt x)) (right' eta)
56+{-# INLINE prism #-}
57+
58+prismRavel :: (Market a b a b -> Market a b s t) -> Prism s t a b
59+prismRavel l pab = (prism2prismvl $ l idPrism) pab
60+{-# INLINE prismRavel #-}
61+
62+type APrismVL s t a b = Market a b a (Identity b) -> Market a b s (Identity t)
63+
64+withPrism :: APrismVL s t a b -> ((b -> t) -> (s -> Either t a) -> r) -> r
65+withPrism k f = case coerce (k (Market Identity Right)) of
66+ Market bt seta -> f bt seta
67+
68+prism2prismvl :: Market a b s t -> Prism s t a b
69+prism2prismvl (Market bt seta) = prism bt seta
70+{-# INLINE prism2prismvl #-}
71+
72+build :: (Tagged b (Identity b) -> Tagged t (Identity t)) -> b -> t
73+build p = runIdentity #. unTagged #. p .# Tagged .# Identity
74+{-# INLINE build #-}
src/Data/Generics/Internal/VL/Traversal.hsnew file+92
@@ -0,0 +1,92 @@
1+{-# OPTIONS_HADDOCK hide #-}
2+{-# LANGUAGE AllowAmbiguousTypes #-}
3+{-# LANGUAGE ConstraintKinds #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE NoMonomorphismRestriction #-}
6+{-# LANGUAGE Rank2Types #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeFamilies #-}
9+{-# LANGUAGE TypeFamilyDependencies #-}
10+{-# LANGUAGE TypeOperators #-}
11+
12+-----------------------------------------------------------------------------
13+-- |
14+-- Module : Data.Generics.Internal.VL.Traversal
15+-- Copyright : (C) 2019 Csongor Kiss
16+-- License : BSD3
17+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
18+-- Stability : experimental
19+-- Portability : non-portable
20+--
21+-- Internal lens helpers. Only exported for Haddock
22+--
23+-----------------------------------------------------------------------------
24+module Data.Generics.Internal.VL.Traversal where
25+
26+import Data.Kind (Constraint)
27+
28+-- | Type alias for traversal
29+type Traversal' s a
30+ = forall f. Applicative f => (a -> f a) -> s -> f s
31+
32+type TraversalC (c :: * -> * -> Constraint) s t
33+ = forall f. Applicative f => (forall a b. c a b => a -> f b) -> s -> f t
34+
35+type TraversalC' (c :: * -> Constraint) s
36+ = forall f. Applicative f => (forall a. c a => a -> f a) -> s -> f s
37+
38+type Traversal s t a b
39+ = forall f. Applicative f => (a -> f b) -> s -> f t
40+
41+type LensLikeC c f s
42+ = (forall a. c a => a -> f a) -> s -> f s
43+
44+confusing :: Applicative f => Traversal s t a b -> (a -> f b) -> s -> f t
45+confusing t = \f -> lowerYoneda . lowerCurried . t (liftCurriedYoneda . f)
46+{-# INLINE confusing #-}
47+
48+-- fuse constrained traversals
49+confusingC :: forall c f s. Applicative f => TraversalC' c s -> LensLikeC c f s
50+confusingC t = \f -> lowerYoneda . lowerCurried . t (liftCurriedYoneda . f)
51+{-# INLINE confusingC #-}
52+
53+liftCurriedYoneda :: Applicative f => f a -> Curried (Yoneda f) a
54+liftCurriedYoneda fa = Curried (`yap` fa)
55+{-# INLINE liftCurriedYoneda #-}
56+
57+yap :: Applicative f => Yoneda f (a -> b) -> f a -> Yoneda f b
58+yap (Yoneda k) fa = Yoneda (\ab_r -> k (ab_r .) <*> fa)
59+{-# INLINE yap #-}
60+
61+newtype Curried f a =
62+ Curried { runCurried :: forall r. f (a -> r) -> f r }
63+
64+instance Functor f => Functor (Curried f) where
65+ fmap f (Curried g) = Curried (g . fmap (.f))
66+ {-# INLINE fmap #-}
67+
68+instance (Functor f) => Applicative (Curried f) where
69+ pure a = Curried (fmap ($ a))
70+ {-# INLINE pure #-}
71+ Curried mf <*> Curried ma = Curried (ma . mf . fmap (.))
72+ {-# INLINE (<*>) #-}
73+
74+liftCurried :: Applicative f => f a -> Curried f a
75+liftCurried fa = Curried (<*> fa)
76+
77+lowerCurried :: Applicative f => Curried f a -> f a
78+lowerCurried (Curried f) = f (pure id)
79+newtype Yoneda f a = Yoneda { runYoneda :: forall b. (a -> b) -> f b }
80+
81+liftYoneda :: Functor f => f a -> Yoneda f a
82+liftYoneda a = Yoneda (\f -> fmap f a)
83+
84+lowerYoneda :: Yoneda f a -> f a
85+lowerYoneda (Yoneda f) = f id
86+
87+instance Functor (Yoneda f) where
88+ fmap f m = Yoneda (\k -> runYoneda m (k . f))
89+
90+instance Applicative f => Applicative (Yoneda f) where
91+ pure a = Yoneda (\f -> pure (f a))
92+ Yoneda m <*> Yoneda n = Yoneda (\f -> m (f .) <*> n id)
src/Data/Generics/Internal/Void.hsnew file+19
@@ -0,0 +1,19 @@
1+{-# LANGUAGE PolyKinds #-}
2+module Data.Generics.Internal.Void where
3+
4+{-
5+ Note [Uncluttering type signatures]
6+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7+
8+ Because the various instances in the library always match (the Has* classes
9+ are essentially glorified constraint synonyms), they get replaced with
10+ their constraints, resulting in large, unreadable types.
11+
12+ Writing an (overlapping instance) for this Void type means that the original
13+ instance might not be the one selected, thus GHC leaves the constraints in
14+ place until further information is provided, at which point the type
15+ machinery has sufficient information to reduce to sensible types.
16+-}
17+data Void
18+data Void1 a
19+data Void2 a b
src/Data/Generics/Labels.hsnew file+142
@@ -0,0 +1,142 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE ConstraintKinds #-}
4+{-# LANGUAGE DataKinds #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE TypeApplications #-}
11+{-# LANGUAGE TypeOperators #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE UndecidableInstances #-}
14+{-# OPTIONS_GHC -Wno-orphans #-}
15+
16+--------------------------------------------------------------------------------
17+-- |
18+-- Module : Data.Generics.Labels
19+-- Copyright : (C) 2019 Csongor Kiss
20+-- License : BSD3
21+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
22+-- Stability : experimental
23+-- Portability : non-portable
24+--
25+-- Provides an (orphan) IsLabel instance for field lenses and constructor prisms.
26+-- Use at your own risk.
27+--------------------------------------------------------------------------------
28+
29+module Data.Generics.Labels
30+ ( -- * Orphan IsLabel Instance
31+ -- $sec1
32+ Field(..)
33+ , Field'
34+ , Constructor(..)
35+ , Constructor'
36+ ) where
37+
38+import Data.Generics.Product
39+import Data.Generics.Sum
40+import Data.Generics.Internal.VL.Lens (Lens)
41+import Data.Generics.Internal.VL.Prism (Prism)
42+
43+import Data.Profunctor (Choice)
44+import Data.Type.Bool (type (&&))
45+import Data.Type.Equality (type (==))
46+
47+import GHC.OverloadedLabels
48+import GHC.TypeLits
49+
50+-- $sec1
51+-- An instance for creating lenses and prisms with @#identifiers@ from the
52+-- @OverloadedLabels@ extension. Note that since overloaded labels do not
53+-- support symbols starting with capital letters, all prisms (which come from
54+-- constructor names, which are capitalized) must be prefixed with an underscore
55+-- (e.g. @#_ConstructorName@).
56+--
57+-- Morally:
58+--
59+-- @
60+-- instance (HasField name s t a b) => IsLabel name (Lens s t a b) where ...
61+-- @
62+-- and
63+--
64+-- @
65+-- instance (AsConstructor name s t a b) => IsLabel name (Prism s t a b) where ...
66+-- @
67+--
68+-- Remember:
69+--
70+-- @
71+-- type Lens = forall f. Functor f => (a -> f b) -> s -> f t
72+--
73+-- type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)
74+-- @
75+--
76+-- The orphan instance is unavoidable if we want to work with
77+-- lenses-as-functions (as opposed to a 'ReifiedLens'-like newtype).
78+
79+-- | 'Field' is morally the same as 'HasField', but it is constructed from an
80+-- incoherent combination of 'HasField' and 'HasField''. In this way, it can be
81+-- seamlessly used in the 'IsLabel' instance even when dealing with data types
82+-- that don't have 'Field' instances (like data instances).
83+class Field name s t a b | s name -> a, t name -> b, s name b -> t, t name a -> s where
84+ fieldLens :: Lens s t a b
85+
86+type Field' name s a = Field name s s a a
87+
88+instance {-# INCOHERENT #-} HasField name s t a b => Field name s t a b where
89+ fieldLens = field @name
90+
91+instance {-# INCOHERENT #-} HasField' name s a => Field name s s a a where
92+ fieldLens = field' @name
93+
94+-- | 'Constructor' is morally the same as 'AsConstructor', but it is constructed from an
95+-- incoherent combination of 'AsConstructor' and 'AsConstructor''. In this way, it can be
96+-- seamlessly used in the 'IsLabel' instance even when dealing with data types
97+-- that don't have 'Constructor' instances (like data instances).
98+class Constructor name s t a b | name s -> a, name t -> b where
99+ constructorPrism :: Prism s t a b
100+
101+type Constructor' name s a = Constructor name s s a a
102+
103+instance {-# INCOHERENT #-} AsConstructor name s t a b => Constructor name s t a b where
104+ constructorPrism = _Ctor @name
105+
106+instance {-# INCOHERENT #-} AsConstructor' name s a => Constructor name s s a a where
107+ constructorPrism = _Ctor' @name
108+
109+type family BeginsWithCapital (name :: Symbol) :: Bool where
110+ BeginsWithCapital name = CmpSymbol "_@" name == 'LT && CmpSymbol "_[" name == 'GT
111+
112+instance ( capital ~ BeginsWithCapital name
113+ , IsLabelHelper capital name p f s t a b
114+ , pafb ~ p a (f b), psft ~ p s (f t)) => IsLabel name (pafb -> psft) where
115+#if __GLASGOW_HASKELL__ >= 802
116+ fromLabel = labelOutput @capital @name @p @f
117+#else
118+ fromLabel _ = labelOutput @capital @name @p @f
119+#endif
120+
121+-- | This helper class allows us to customize the output type of the lens to be
122+-- either 'Prism' or 'Lens' (by choosing appropriate @p@ and @f@) as well as to
123+-- choose between whether we're dealing with a lens or a prism. The choice is
124+-- made by whether the @capital@ argument is true or false, which is determined by
125+-- whether the symbol starts with an underscore followed by a capital letter
126+-- (a check done in the 'IsLabel' instance above). If so, then we're dealing
127+-- with a constructor name, which should be a prism, and otherwise, it's a field
128+-- name, so we have a lens.
129+class IsLabelHelper capital name p f s t a b where
130+ labelOutput :: p a (f b) -> p s (f t)
131+
132+instance (Functor f, Field name s t a b) => IsLabelHelper 'False name (->) f s t a b where
133+ labelOutput = fieldLens @name
134+
135+#if __GLASGOW_HASKELL__ >= 802
136+instance ( Applicative f, Choice p, Constructor name s t a b
137+ , name' ~ AppendSymbol "_" name) => IsLabelHelper 'True name' p f s t a b where
138+ labelOutput = constructorPrism @name
139+#else
140+instance (TypeError ('Text "Labels for Prisms require at least GHC 8.2"), Choice p) => IsLabelHelper 'True name' p f s t a b where
141+ labelOutput = undefined
142+#endif
src/Data/Generics/Product.hs+15−9
@@ -1,7 +1,7 @@
11-----------------------------------------------------------------------------
22-- |
33-- Module : Data.Generics.Product
4−-- Copyright : (C) 2017 Csongor Kiss
4+-- Copyright : (C) 2019 Csongor Kiss
55-- License : BSD3
66-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
77-- Stability : experimental
@@ -17,14 +17,16 @@
1717
1818module Data.Generics.Product
1919 ( -- *Lenses
20− HasAny (..)
21−
22− , HasField (..)
23− , HasPosition (..)
24− , HasType (..)
25−
26− -- *Subtype relationships
27− , Subtype (..)
20+ module Data.Generics.Product.Any
21+ , module Data.Generics.Product.Fields
22+ , module Data.Generics.Product.Positions
23+ , module Data.Generics.Product.Subtype
24+ , module Data.Generics.Product.Typed
25+ , module Data.Generics.Product.HList
26+ -- *Traversals
27+ , module Data.Generics.Product.Types
28+ , module Data.Generics.Product.Param
29+ , module Data.Generics.Product.Constraints
2830 ) where
2931
3032import Data.Generics.Product.Any
@@ -32,3 +34,7 @@ import Data.Generics.Product.Fields
3234import Data.Generics.Product.Positions
3335import Data.Generics.Product.Subtype
3436import Data.Generics.Product.Typed
37+import Data.Generics.Product.Types
38+import Data.Generics.Product.Constraints
39+import Data.Generics.Product.Param
40+import Data.Generics.Product.HList
src/Data/Generics/Product/Any.hs+28−25
@@ -6,12 +6,13 @@
66{-# LANGUAGE PolyKinds #-}
77{-# LANGUAGE ScopedTypeVariables #-}
88{-# LANGUAGE TypeApplications #-}
9+{-# LANGUAGE TypeFamilies #-}
910{-# LANGUAGE UndecidableInstances #-}
1011
1112-----------------------------------------------------------------------------
1213-- |
1314-- Module : Data.Generics.Product.Any
14−-- Copyright : (C) 2017 Csongor Kiss
15+-- Copyright : (C) 2019 Csongor Kiss
1516-- License : BSD3
1617-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1718-- Stability : experimental
@@ -24,35 +25,35 @@
2425module Data.Generics.Product.Any
2526 ( -- *Lenses
2627 --
27− -- $example
28+ -- $setup
2829 HasAny (..)
2930 ) where
3031
31−import Data.Generics.Internal.Lens
32+import Data.Generics.Internal.VL.Lens
3233import Data.Generics.Product.Fields
3334import Data.Generics.Product.Positions
3435import Data.Generics.Product.Typed
3536
36−-- $example
37−-- @
38−-- module Example where
37+-- $setup
38+-- == /Running example:/
3939--
40−-- import Data.Generics.Product
41−-- import GHC.Generics
42−--
43−-- data Human = Human
44−-- { name :: String
45−-- , age :: Int
46−-- , address :: String
47−-- }
48−-- deriving (Generic, Show)
49−--
50−-- human :: Human
51−-- human = Human \"Tunyasz\" 50 \"London\"
52−-- @
40+-- >>> :set -XTypeApplications
41+-- >>> :set -XDataKinds
42+-- >>> :set -XDeriveGeneric
43+-- >>> import GHC.Generics
44+-- >>> :m +Data.Generics.Internal.VL.Lens
45+-- >>> :{
46+-- data Human = Human
47+-- { name :: String
48+-- , age :: Int
49+-- , address :: String
50+-- }
51+-- deriving (Generic, Show)
52+-- human :: Human
53+-- human = Human "Tunyasz" 50 "London"
54+-- :}
5355
54−-- |Records that have generic lenses.
55−class HasAny (sel :: k) a s | s sel k -> a where
56+class HasAny sel s t a b | s sel -> a where
5657 -- |A lens that focuses on a part of a product as identified by some
5758 -- selector. Currently supported selectors are field names, positions and
5859 -- unique types. Compatible with the lens package's 'Control.Lens.Lens'
@@ -60,17 +61,19 @@ class HasAny (sel :: k) a s | s sel k -> a where
6061 --
6162 -- >>> human ^. the @Int
6263 -- 50
64+ --
6365 -- >>> human ^. the @"name"
6466 -- "Tunyasz"
67+ --
6568 -- >>> human ^. the @3
6669 -- "London"
67− the :: Lens' s a
70+ the :: Lens s t a b
6871
69−instance HasPosition i a s => HasAny i a s where
72+instance HasPosition i s t a b => HasAny i s t a b where
7073 the = position @i
7174
72−instance HasField field a s => HasAny field a s where
75+instance HasField field s t a b => HasAny field s t a b where
7376 the = field @field
7477
75−instance HasType a s => HasAny a a s where
78+instance (HasType a s, t ~ s, a ~ b) => HasAny a s t a b where
7679 the = typed @a
src/Data/Generics/Product/Constraints.hsnew file+57
@@ -0,0 +1,57 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE KindSignatures #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE Rank2Types #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeApplications #-}
9+{-# LANGUAGE UndecidableInstances #-}
10+
11+-----------------------------------------------------------------------------
12+-- |
13+-- Module : Data.Generics.Product.Constraints
14+-- Copyright : (C) 2019 Csongor Kiss
15+-- License : BSD3
16+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17+-- Stability : experimental
18+-- Portability : non-portable
19+--
20+-- Constrained traversals.
21+--
22+-----------------------------------------------------------------------------
23+
24+module Data.Generics.Product.Constraints
25+ ( -- *Traversals
26+ --
27+ -- $example
28+ HasConstraints (..)
29+ , HasConstraints' (..)
30+ ) where
31+
32+import Data.Generics.Product.Internal.Constraints
33+import Data.Kind (Constraint)
34+
35+import GHC.Generics (Generic (Rep), from, to)
36+import Data.Generics.Internal.VL.Traversal
37+
38+class HasConstraints' (c :: * -> Constraint) s where
39+ constraints' :: TraversalC' c s
40+
41+instance
42+ ( Generic s
43+ , GHasConstraints' c (Rep s)
44+ ) => HasConstraints' c s where
45+ constraints' = confusingC @c (\f s -> to <$> gconstraints' @c f (from s))
46+ {-# INLINE constraints' #-}
47+
48+class HasConstraints (c :: * -> * -> Constraint) s t where
49+ constraints :: TraversalC c s t
50+
51+instance
52+ ( Generic s
53+ , Generic t
54+ , GHasConstraints c (Rep s) (Rep t)
55+ ) => HasConstraints c s t where
56+ constraints f s = to <$> gconstraints @c f (from s)
57+ {-# INLINE constraints #-}
src/Data/Generics/Product/Fields.hs+193−59
@@ -1,19 +1,22 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE ConstraintKinds #-}
4+{-# LANGUAGE DataKinds #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE TypeApplications #-}
10+{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeInType #-}
12+{-# LANGUAGE TypeOperators #-}
13+{-# LANGUAGE UndecidableInstances #-}
14+{-# LANGUAGE UndecidableSuperClasses #-}
1215
1316-----------------------------------------------------------------------------
1417-- |
1518-- Module : Data.Generics.Product.Fields
16−-- Copyright : (C) 2017 Csongor Kiss
19+-- Copyright : (C) 2019 Csongor Kiss
1720-- License : BSD3
1821-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1922-- Stability : experimental
@@ -26,81 +29,212 @@
2629module Data.Generics.Product.Fields
2730 ( -- *Lenses
2831
29− -- $example
32+ -- $setup
3033 HasField (..)
34+ , HasField' (..)
35+ , HasField_ (..)
36+
37+ , getField
38+ , setField
3139 ) where
3240
3341import Data.Generics.Internal.Families
34−import Data.Generics.Internal.Lens
35−import Data.Generics.Product.Internal.Fields
42+import Data.Generics.Internal.VL.Lens as VL
43+import Data.Generics.Internal.Void
44+import Data.Generics.Product.Internal.GLens
3645
3746import Data.Kind (Constraint, Type)
3847import GHC.Generics
3948import GHC.TypeLits (Symbol, ErrorMessage(..), TypeError)
49+import Data.Generics.Internal.Profunctor.Lens as P
50+import Data.Generics.Internal.Errors
4051
41−-- $example
42−-- @
43−-- module Example where
44−--
45−-- import Data.Generics.Product
46−-- import GHC.Generics
47−--
48−-- data Human = Human
49−-- { name :: String
50−-- , age :: Int
51−-- , address :: String
52−-- }
53−-- deriving (Generic, Show)
52+-- $setup
53+-- == /Running example:/
5454--
55−-- human :: Human
56−-- human = Human \"Tunyasz\" 50 \"London\"
57−-- @
55+-- >>> :set -XTypeApplications
56+-- >>> :set -XDataKinds
57+-- >>> :set -XDeriveGeneric
58+-- >>> :set -XGADTs
59+-- >>> :set -XFlexibleContexts
60+-- >>> import GHC.Generics
61+-- >>> :m +Data.Generics.Internal.VL.Lens
62+-- >>> :m +Data.Function
63+-- >>> :{
64+-- data Human a
65+-- = Human
66+-- { name :: String
67+-- , age :: Int
68+-- , address :: String
69+-- , other :: a
70+-- }
71+-- | HumanNoAddress
72+-- { name :: String
73+-- , age :: Int
74+-- , other :: a
75+-- }
76+-- deriving (Generic, Show)
77+-- human :: Human Bool
78+-- human = Human { name = "Tunyasz", age = 50, address = "London", other = False }
79+-- :}
5880
5981-- |Records that have a field with a given name.
60−class HasField (field :: Symbol) a s | s field -> a where
82+class HasField (field :: Symbol) s t a b | s field -> a, t field -> b, s field b -> t, t field a -> s where
6183 -- |A lens that focuses on a field with a given name. Compatible with the
6284 -- lens package's 'Control.Lens.Lens' type.
6385 --
6486 -- >>> human ^. field @"age"
6587 -- 50
66− -- >>> human & field @"name" .~ "Tamas"
67− -- Human {name = "Tamas", age = 50, address = "London"}
68− field :: Lens' s a
69− field f s
70− = fmap (flip (setField @field) s) (f (getField @field s))
71−
72− -- |Get 'field'
7388 --
74− -- >>> getField @"name" human
75− -- "Tunyasz"
76− getField :: s -> a
77− getField s = s ^. field @field
78−
79− -- |Set 'field'
89+ -- === /Type changing/
90+ --
91+ -- >>> :t human
92+ -- human :: Human Bool
93+ --
94+ -- >>> :t human & field @"other" .~ (42 :: Int)
95+ -- human & field @"other" .~ (42 :: Int) :: Human Int
96+ --
97+ -- >>> human & field @"other" .~ 42
98+ -- Human {name = "Tunyasz", age = 50, address = "London", other = 42}
99+ --
100+ -- === /Type errors/
80101 --
81− -- >>> setField @"age" (setField @"name" "Tamas" human) 30
82− -- Human {name = "Tamas", age = 30, address = "London"}
83− setField :: a -> s -> s
84− setField = set (field @field)
102+ -- >>> human & field @"weight" .~ 42
103+ -- ...
104+ -- ... The type Human Bool does not contain a field named 'weight'.
105+ -- ...
106+ --
107+ -- >>> human & field @"address" .~ ""
108+ -- ...
109+ -- ... Not all constructors of the type Human Bool
110+ -- ... contain a field named 'address'.
111+ -- ... The offending constructors are:
112+ -- ... HumanNoAddress
113+ -- ...
114+ field :: VL.Lens s t a b
85115
86− {-# MINIMAL field | setField, getField #-}
116+-- |Records that have a field with a given name.
117+--
118+-- This is meant to be more general than 'HasField', but that is not quite the
119+-- case due to the lack of functional dependencies.
120+--
121+-- The types @s@ and @t@ must be applications of the same type constructor.
122+-- In contrast, 'HasField' also requires the parameters of that type constructor
123+-- to have representational roles.
124+--
125+-- One use case of 'HasField_' over 'HasField' is for records defined with
126+-- @data instance@.
127+class HasField_ (field :: Symbol) s t a b where
128+ field_ :: VL.Lens s t a b
129+
130+class HasField' (field :: Symbol) s a | s field -> a where
131+ field' :: VL.Lens s s a a
132+
133+-- |Records that have a field with a given name.
134+--
135+-- This class gives the minimal constraints needed to define this lens.
136+-- For common uses, see 'HasField'.
137+class HasField0 (field :: Symbol) s t a b where
138+ field0 :: VL.Lens s t a b
139+
140+-- |
141+-- >>> getField @"age" human
142+-- 50
143+getField :: forall f a s. HasField' f s a => s -> a
144+getField = VL.view (field' @f)
145+
146+-- |
147+-- >>> setField @"age" 60 human
148+-- Human {name = "Tunyasz", age = 60, address = "London", other = False}
149+setField :: forall f s a. HasField' f s a => a -> s -> s
150+setField = VL.set (field' @f)
87151
88152instance
89153 ( Generic s
90− , ErrorUnless field s (HasTotalFieldP field (Rep s))
91− , GHasField field (Rep s) a
92− ) => HasField field a s where
154+ , ErrorUnless field s (CollectField field (Rep s))
155+ , GLens' (HasTotalFieldPSym field) (Rep s) a
156+ , Defined (Rep s)
157+ (NoGeneric s '[ 'Text "arising from a generic lens focusing on the "
158+ ':<>: QuoteType field ':<>: 'Text " field of type " ':<>: QuoteType a
159+ , 'Text "in " ':<>: QuoteType s])
160+ (() :: Constraint)
161+ ) => HasField' field s a where
162+ field' f s = field0 @field f s
163+
164+class (~~) (a :: k) (b :: k) | a -> b, b -> a
165+instance (a ~ b) => (~~) a b
166+
167+instance -- see Note [Changing type parameters]
168+ ( HasTotalFieldP field (Rep s) ~~ 'Just a
169+ , HasTotalFieldP field (Rep t) ~~ 'Just b
170+ , HasTotalFieldP field (Rep (Indexed s)) ~~ 'Just a'
171+ , HasTotalFieldP field (Rep (Indexed t)) ~~ 'Just b'
172+ , t ~~ Infer s a' b
173+ , s ~~ Infer t b' a
174+ , HasField0 field s t a b
175+ ) => HasField field s t a b where
176+ field f s = field0 @field f s
177+
178+-- | See Note [Uncluttering type signatures]
179+#if __GLASGOW_HASKELL__ < 804
180+-- >>> :t field
181+-- field
182+-- :: (HasField field s t a b, Functor f) => (a -> f b) -> s -> f t
183+#else
184+-- >>> :t field
185+-- field
186+-- :: (Functor f, HasField field s t a b) => (a -> f b) -> s -> f t
187+#endif
188+instance {-# OVERLAPPING #-} HasField f (Void1 a) (Void1 b) a b where
189+ field = undefined
190+
191+instance
192+ ( HasTotalFieldP field (Rep s) ~~ 'Just a
193+ , HasTotalFieldP field (Rep t) ~~ 'Just b
194+ , UnifyHead s t
195+ , UnifyHead t s
196+ , HasField0 field s t a b
197+ ) => HasField_ field s t a b where
198+ field_ f s = field0 @field f s
93199
94− field = ravel (repIso . gfield @field)
200+instance {-# OVERLAPPING #-} HasField_ f (Void1 a) (Void1 b) a b where
201+ field_ = undefined
95202
96−type family ErrorUnless (field :: Symbol) (s :: Type) (contains :: Bool) :: Constraint where
97− ErrorUnless field s 'False
203+instance
204+ ( Generic s
205+ , Generic t
206+ , GLens (HasTotalFieldPSym field) (Rep s) (Rep t) a b
207+ , ErrorUnless field s (CollectField field (Rep s))
208+ , Defined (Rep s)
209+ (NoGeneric s '[ 'Text "arising from a generic lens focusing on the "
210+ ':<>: QuoteType field ':<>: 'Text " field of type " ':<>: QuoteType a
211+ , 'Text "in " ':<>: QuoteType s])
212+ (() :: Constraint)
213+ ) => HasField0 field s t a b where
214+ field0 = VL.ravel (repLens . glens @(HasTotalFieldPSym field))
215+ {-# INLINE field0 #-}
216+
217+type family ErrorUnless (field :: Symbol) (s :: Type) (stat :: TypeStat) :: Constraint where
218+ ErrorUnless field s ('TypeStat _ _ '[])
98219 = TypeError
99220 ( 'Text "The type "
100221 ':<>: 'ShowType s
101− ':<>: 'Text " does not contain a field named "
102− ':<>: 'ShowType field
222+ ':<>: 'Text " does not contain a field named '"
223+ ':<>: 'Text field ':<>: 'Text "'."
224+ )
225+
226+ ErrorUnless field s ('TypeStat (n ': ns) _ _)
227+ = TypeError
228+ ( 'Text "Not all constructors of the type "
229+ ':<>: 'ShowType s
230+ ':$$: 'Text " contain a field named '"
231+ ':<>: 'Text field ':<>: 'Text "'."
232+ ':$$: 'Text "The offending constructors are:"
233+ ':$$: ShowSymbols (n ': ns)
103234 )
104235
105− ErrorUnless _ _ 'True
236+ ErrorUnless _ _ ('TypeStat '[] '[] _)
106237 = ()
238+
239+data HasTotalFieldPSym :: Symbol -> (TyFun (Type -> Type) (Maybe Type))
240+type instance Eval (HasTotalFieldPSym sym) tt = HasTotalFieldP sym tt
src/Data/Generics/Product/HList.hsnew file+46
@@ -0,0 +1,46 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE MonoLocalBinds #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE TypeInType #-}
8+{-# LANGUAGE UndecidableInstances #-}
9+
10+-----------------------------------------------------------------------------
11+-- |
12+-- Module : Data.Generics.Product.HList
13+-- Copyright : (C) 2019 Csongor Kiss
14+-- License : BSD3
15+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
16+-- Stability : experimental
17+-- Portability : non-portable
18+--
19+-- Derive an isomorphism between a product type and a flat HList.
20+--
21+-----------------------------------------------------------------------------
22+
23+module Data.Generics.Product.HList
24+ ( IsList (..)
25+ ) where
26+
27+import Data.Generics.Product.Internal.HList
28+import Data.Kind
29+import GHC.Generics
30+import Data.Generics.Internal.VL.Iso (Iso)
31+import Data.Generics.Internal.Profunctor.Iso (repIso, iso2isovl)
32+
33+class IsList
34+ (f :: Type)
35+ (g :: Type)
36+ (as :: [Type])
37+ (bs :: [Type]) | f -> as, g -> bs where
38+ list :: Iso f g (HList as) (HList bs)
39+
40+instance
41+ ( Generic f
42+ , Generic g
43+ , GIsList (Rep f) (Rep g) as bs
44+ ) => IsList f g as bs where
45+ list = iso2isovl (repIso . glist)
46+ {-# INLINE list #-}
src/Data/Generics/Product/Internal/Constraints.hsnew file+136
@@ -0,0 +1,136 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
7+{-# LANGUAGE GADTs #-}
8+{-# LANGUAGE Rank2Types #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE TypeApplications #-}
11+{-# LANGUAGE TypeFamilyDependencies #-}
12+{-# LANGUAGE TypeOperators #-}
13+{-# LANGUAGE UndecidableInstances #-}
14+
15+-----------------------------------------------------------------------------
16+-- |
17+-- Module : Data.Generics.Product.Internal.Constraints
18+-- Copyright : (C) 2019 Csongor Kiss
19+-- License : BSD3
20+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21+-- Stability : experimental
22+-- Portability : non-portable
23+--
24+-- Derive constrained traversals
25+--
26+-----------------------------------------------------------------------------
27+
28+-- TODO: use type-changing variant
29+
30+module Data.Generics.Product.Internal.Constraints
31+ (
32+ GHasConstraints (..)
33+ , GHasConstraints' (..)
34+ ) where
35+
36+import Data.Kind (Type, Constraint)
37+import GHC.Generics
38+import Data.Generics.Internal.VL.Iso
39+import Data.Generics.Internal.VL.Traversal
40+import Data.Generics.Product.Internal.HList
41+
42+-- | Constrained traversal.
43+class GHasConstraints' (c :: * -> Constraint) (f :: * -> *) where
44+ gconstraints' :: forall g x.
45+ Applicative g => (forall a. c a => a -> g a) -> f x -> g (f x)
46+
47+instance
48+ ( GHasConstraints' c l
49+ , GHasConstraints' c r
50+ ) => GHasConstraints' c (l :*: r) where
51+ gconstraints' f (l :*: r)
52+ = (:*:) <$> gconstraints' @c f l <*> gconstraints' @c f r
53+
54+instance
55+ ( GHasConstraints' c l
56+ , GHasConstraints' c r
57+ ) => GHasConstraints' c (l :+: r) where
58+ gconstraints' f (L1 l) = L1 <$> gconstraints' @c f l
59+ gconstraints' f (R1 r) = R1 <$> gconstraints' @c f r
60+
61+instance c a => GHasConstraints' c (Rec0 a) where
62+ gconstraints' = kIso
63+
64+instance GHasConstraints' c f
65+ => GHasConstraints' c (M1 m meta f) where
66+ gconstraints' f (M1 x) = M1 <$> gconstraints' @c f x
67+
68+instance GHasConstraints' c U1 where
69+ gconstraints' _ _ = pure U1
70+
71+--------------------------------------------------------------------------------
72+
73+class GHasConstraints (c :: * -> * -> Constraint) s t where
74+ gconstraints :: TraversalC c (s x) (t x)
75+
76+instance
77+ ( GHasConstraints c l l'
78+ , GHasConstraints c r r'
79+ ) => GHasConstraints c (l :*: r) (l' :*: r') where
80+ gconstraints f (l :*: r)
81+ = (:*:) <$> gconstraints @c f l <*> gconstraints @c f r
82+
83+instance
84+ ( GHasConstraints c l l'
85+ , GHasConstraints c r r'
86+ ) => GHasConstraints c (l :+: r) (l' :+: r') where
87+ gconstraints f (L1 l) = L1 <$> gconstraints @c f l
88+ gconstraints f (R1 r) = R1 <$> gconstraints @c f r
89+
90+instance GHasConstraints c s t
91+ => GHasConstraints c (M1 i m s) (M1 i m t) where
92+ gconstraints f (M1 x) = M1 <$> gconstraints @c f x
93+
94+instance GHasConstraints c U1 U1 where
95+ gconstraints _ _ = pure U1
96+
97+instance GHasConstraints c V1 V1 where
98+ gconstraints _ = pure
99+
100+instance c a b => GHasConstraints c (Rec0 a) (Rec0 b) where
101+ gconstraints = kIso
102+
103+--------------------------------------------------------------------------------
104+
105+-- | Multi-traversal
106+--type GHasTypes rep as = GHasConstraints (Contains as) rep
107+
108+-- Try to use a function from the list, or default to 'pure' if not present
109+{-
110+gtypes
111+ :: forall as x f g.
112+ ( GHasTypes f as
113+ , Applicative g
114+ ) => HList (Functions as g) -> f x -> g (f x)
115+gtypes hl s = gconstraints @(Contains as) (pick hl) s
116+-}
117+
118+--------------------------------------------------------------------------------
119+
120+-- >>> :kind! Functions '[Int, Char, Bool] Maybe
121+-- '[Int -> Maybe Int, Char -> Maybe Char, Bool -> Maybe Bool]
122+type family Functions (ts :: [Type]) (g :: Type -> Type) = r | r -> ts where
123+ Functions '[] _ = '[]
124+ Functions (t ': ts) g = ((t -> g t) ': Functions ts g)
125+
126+class Contains as a where
127+ pick :: Applicative g => HList (Functions as g) -> a -> g a
128+
129+instance {-# OVERLAPPING #-} Contains (a ': as) a where
130+ pick (h :> _) = h
131+
132+instance Contains as a => Contains (b ': as) a where
133+ pick (_ :> hs) = pick hs
134+
135+instance Contains '[] a where
136+ pick _ = pure
src/Data/Generics/Product/Internal/Fields.hsdeleted−71
@@ -1,71 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−
13−-----------------------------------------------------------------------------
14−-- |
15−-- Module : Data.Generics.Product.Internal.Fields
16−-- Copyright : (C) 2017 Csongor Kiss
17−-- License : BSD3
18−-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
19−-- Stability : experimental
20−-- Portability : non-portable
21−--
22−-- Derive record field getters and setters generically.
23−--
24−-----------------------------------------------------------------------------
25−
26−module Data.Generics.Product.Internal.Fields
27− ( GHasField (..)
28− ) where
29−
30−import Data.Generics.Internal.Families
31−import Data.Generics.Internal.Lens
32−
33−import Data.Kind (Type)
34−import GHC.Generics
35−import GHC.TypeLits (Symbol)
36−
37−-- |As 'HasField' but over generic representations as defined by
38−-- "GHC.Generics".
39−class GHasField (field :: Symbol) (f :: Type -> Type) a | field f -> a where
40− gfield :: Lens' (f x) a
41−
42−instance GProductHasField field l r a (HasTotalFieldP field l)
43− => GHasField field (l :*: r) a where
44−
45− gfield = gproductField @field @_ @_ @_ @(HasTotalFieldP field l)
46−
47−instance (GHasField field l a, GHasField field r a)
48− => GHasField field (l :+: r) a where
49−
50− gfield = combine (gfield @field @l) (gfield @field @r)
51−
52−instance GHasField field (K1 R a) a where
53− gfield f (K1 x) = fmap K1 (f x)
54−
55−instance GHasField field (S1 ('MetaSel ('Just field) upkd str infstr) (Rec0 a)) a where
56− gfield = mIso . gfield @field
57−
58−instance GHasField field f a => GHasField field (M1 D meta f) a where
59− gfield = mIso . gfield @field
60−
61−instance GHasField field f a => GHasField field (M1 C meta f) a where
62− gfield = mIso . gfield @field
63−
64−class GProductHasField (field :: Symbol) l r a (left :: Bool) | left field l r -> a where
65− gproductField :: Lens' ((l :*: r) x) a
66−
67−instance GHasField field l a => GProductHasField field l r a 'True where
68− gproductField = first . gfield @field
69−
70−instance GHasField field r a => GProductHasField field l r a 'False where
71− gproductField = second . gfield @field
src/Data/Generics/Product/Internal/GLens.hsnew file+81
@@ -0,0 +1,81 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE TypeInType #-}
3+{-# LANGUAGE AllowAmbiguousTypes #-}
4+{-# LANGUAGE ConstraintKinds #-}
5+{-# LANGUAGE DataKinds #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE FunctionalDependencies #-}
8+{-# LANGUAGE KindSignatures #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE Rank2Types #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE TypeFamilies #-}
14+{-# LANGUAGE TypeOperators #-}
15+{-# LANGUAGE UndecidableInstances #-}
16+
17+-----------------------------------------------------------------------------
18+-- |
19+-- Module : Data.Generics.Product.Internal.GLens
20+-- Copyright : (C) 2019 Csongor Kiss
21+-- License : BSD3
22+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
23+-- Stability : experimental
24+-- Portability : non-portable
25+--
26+-- Derive record field getters and setters generically.
27+--
28+-----------------------------------------------------------------------------
29+
30+module Data.Generics.Product.Internal.GLens
31+ ( GLens (..)
32+ , GLens'
33+ , TyFun
34+ , Eval
35+ ) where
36+
37+import Data.Generics.Internal.Profunctor.Lens (Lens, choosing, first, second)
38+import Data.Generics.Internal.Profunctor.Iso (kIso, sumIso, mIso)
39+
40+import Data.Kind (Type)
41+import GHC.Generics
42+
43+type Pred = TyFun (Type -> Type) (Maybe Type)
44+
45+type TyFun a b = a -> b -> Type
46+type family Eval (f :: TyFun a b) (x :: a) :: b
47+
48+-- A generic lens that uses some predicate to determine which field to focus on
49+class GLens (pred :: Pred) (s :: Type -> Type) (t :: Type -> Type) a b | s pred -> a, t pred -> b where
50+ glens :: Lens (s x) (t x) a b
51+
52+type GLens' pred s a = GLens pred s s a a
53+
54+instance GProductLens (Eval pred l) pred l r l' r' a b
55+ => GLens pred (l :*: r) (l' :*: r') a b where
56+
57+ glens = gproductLens @(Eval pred l) @pred
58+ {-# INLINE glens #-}
59+
60+instance (GLens pred l l' a b, GLens pred r r' a b) => GLens pred (l :+: r) (l' :+: r') a b where
61+ glens = sumIso . choosing (glens @pred) (glens @pred)
62+ {-# INLINE glens #-}
63+
64+instance GLens pred (K1 r a) (K1 r b) a b where
65+ glens = kIso
66+ {-# INLINE glens #-}
67+
68+instance (GLens pred f g a b) => GLens pred (M1 m meta f) (M1 m meta g) a b where
69+ glens = mIso . glens @pred
70+ {-# INLINE glens #-}
71+
72+class GProductLens (left :: Maybe Type) (pred :: Pred) l r l' r' a b | pred l r -> a, pred l' r' -> b where
73+ gproductLens :: Lens ((l :*: r) x) ((l' :*: r') x) a b
74+
75+instance GLens pred l l' a b => GProductLens ('Just x) pred l r l' r a b where
76+ gproductLens = first . glens @pred
77+ {-# INLINE gproductLens #-}
78+
79+instance GLens pred r r' a b => GProductLens 'Nothing pred l r l r' a b where
80+ gproductLens = second . glens @pred
81+ {-# INLINE gproductLens #-}
src/Data/Generics/Internal/HList.hs → src/Data/Generics/Product/Internal/HList.hsrenamed+154−64
@@ -1,39 +1,59 @@
1−{-# LANGUAGE DataKinds #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE ConstraintKinds #-}
24{-# LANGUAGE FlexibleInstances #-}
35{-# LANGUAGE FunctionalDependencies #-}
46{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE PolyKinds #-}
87{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TupleSections #-}
9+{-# LANGUAGE TypeApplications #-}
910{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeInType #-}
1012{-# LANGUAGE TypeOperators #-}
1113{-# LANGUAGE UndecidableInstances #-}
1214
15+#if __GLASGOW_HASKELL__ == 802
16+{-# OPTIONS_GHC -fno-solve-constant-dicts #-}
17+#endif
18+
19+
1320-----------------------------------------------------------------------------
1421-- |
15−-- Module : Data.Generics.Internal.HList
16−-- Copyright : (C) 2017 Csongor Kiss
22+-- Module : Data.Generics.Product.Internal.HList
23+-- Copyright : (C) 2019 Csongor Kiss
1724-- License : BSD3
1825-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1926-- Stability : experimental
2027-- Portability : non-portable
2128--
29+-- Derive an isomorphism between a product type and a flat HList.
30+--
2231-----------------------------------------------------------------------------
23−module Data.Generics.Internal.HList
24− ( type (++)
2532
33+module Data.Generics.Product.Internal.HList
34+ ( GIsList(..)
35+ , IndexList (..)
36+ , HList (..)
37+ , type (++)
38+ , Elem
2639 , ListTuple (..)
27−
28− , GCollectible (..)
40+ , TupleToList
2941 ) where
3042
31−import Data.Kind (Type)
43+#if __GLASGOW_HASKELL__ < 804
44+import Data.Semigroup
45+#endif
46+import GHC.TypeLits
47+
48+import Data.Kind (Type)
3249import GHC.Generics
50+import Data.Profunctor
51+import Data.Generics.Internal.Profunctor.Lens
52+import Data.Generics.Internal.Profunctor.Iso
3353
34−data HList (xs :: [Type]) where
35− Nil :: HList '[]
36− (:>) :: x -> HList xs -> HList (x ': xs)
54+data HList (as :: [Type]) where
55+ Nil :: HList '[]
56+ (:>) :: a -> HList as -> HList (a ': as)
3757
3858infixr 5 :>
3959
@@ -41,32 +61,118 @@ type family ((as :: [k]) ++ (bs :: [k])) :: [k] where
4161 '[] ++ bs = bs
4262 (a ': as) ++ bs = a ': as ++ bs
4363
44−append :: HList as -> HList bs -> HList (as ++ bs)
45−append Nil ys = ys
46−append (x :> xs) ys = x :> append xs ys
64+instance Semigroup (HList '[]) where
65+ _ <> _ = Nil
66+
67+instance Monoid (HList '[]) where
68+ mempty = Nil
69+ mappend _ _ = Nil
70+
71+instance (Semigroup a, Semigroup (HList as)) => Semigroup (HList (a ': as)) where
72+ (x :> xs) <> (y :> ys) = (x <> y) :> (xs <> ys)
73+
74+instance (Monoid a, Monoid (HList as)) => Monoid (HList (a ': as)) where
75+ mempty = mempty :> mempty
76+ mappend (x :> xs) (y :> ys) = mappend x y :> mappend xs ys
77+
78+class Elem (as :: [(k, Type)]) (key :: k) (i :: Nat) a | as key -> i a
79+instance {-# OVERLAPPING #-} pos ~ 0 => Elem (a ': xs) key pos a
80+instance (Elem xs key i a, pos ~ (i + 1)) => Elem (x ': xs) key pos a
81+
82+class GIsList
83+ (f :: Type -> Type)
84+ (g :: Type -> Type)
85+ (as :: [Type])
86+ (bs :: [Type]) | f -> as, g -> bs, bs f -> g, as g -> f where
87+
88+ glist :: Iso (f x) (g x) (HList as) (HList bs)
89+
90+ -- We define this reversed version, otherwise uses of `fromIso glist` are not
91+ -- properly inlined by GHC 8.0.2.
92+ -- This is not actually used.
93+ glistR :: Iso (HList bs) (HList as) (g x) (f x)
94+ glistR = fromIso glist
95+
96+instance
97+ ( GIsList l l' as as'
98+ , GIsList r r' bs bs'
99+ , Appending as bs cs as' bs' cs'
100+ , cs ~ (as ++ bs)
101+ , cs' ~ (as' ++ bs')
102+ ) => GIsList (l :*: r) (l' :*: r') cs cs' where
47103
48−head' :: HList (a ': as) -> a
49−head' (x :> _) = x
104+ glist = prodIso . pairing glist glist . appending
105+ {-# INLINE glist #-}
106+
107+instance GIsList f g as bs => GIsList (M1 t meta f) (M1 t meta g) as bs where
108+ glist = mIso . glist
109+ {-# INLINE glist #-}
110+
111+instance GIsList (Rec0 a) (Rec0 b) '[a] '[b] where
112+ glist = kIso . singleton
113+ {-# INLINE glist #-}
114+
115+instance GIsList U1 U1 '[] '[] where
116+ glist = iso (const Nil) (const U1)
117+ {-# INLINE glist #-}
50118
51119--------------------------------------------------------------------------------
52−-- * Split HList
120+-- | as ++ bs === cs
121+class Appending as bs cs as' bs' cs'
122+ | as bs cs cs' -> as' bs'
123+ , as' bs' cs cs' -> as bs
124+ , as bs -> cs
125+ , as' bs' -> cs'
126+ where
127+ appending :: Iso (HList as, HList bs) (HList as', HList bs') (HList cs) (HList cs')
128+
129+-- | [] ++ bs === bs
130+instance Appending '[] bs bs '[] bs' bs' where
131+ appending = iso snd (Nil,)
132+
133+-- | (a : as) ++ bs === (a : cs)
134+instance
135+ Appending as bs cs as' bs' cs' -- as ++ bs == cs
136+ => Appending (a ': as) bs (a ': cs) (a' ': as') bs' (a' ': cs') where
137+ appending
138+ = pairing (fromIso consing) id -- ((a, as), bs)
139+ . assoc3 -- (a, (as, bs))
140+ . pairing id appending -- (a, cs)
141+ . consing -- (a : cs)
53142
54−class Splittable (as :: [Type]) (bs :: [Type]) (cs :: [Type]) | as bs -> cs, as cs -> bs where
55− split :: HList cs -> (HList as, HList bs)
143+singleton :: Iso a b (HList '[a]) (HList '[ b])
144+singleton = iso (:> Nil) (\(x :> _) -> x)
145+
146+consing :: Iso (a, HList as) (b, HList bs) (HList (a ': as)) (HList (b ': bs))
147+consing = iso (\(x, xs) -> x :> xs) (\(x :> xs) -> (x, xs))
148+
149+--------------------------------------------------------------------------------
150+class IndexList (i :: Nat) as bs a b | i as -> a, i bs -> b, i as b -> bs, i bs a -> as where
151+ point :: Lens (HList as) (HList bs) a b
56152
57−instance Splittable '[] bs bs where
58− split bs = (Nil, bs)
153+instance {-# OVERLAPPING #-}
154+ ( as ~ (a ': as')
155+ , bs ~ (b ': as')
156+ ) => IndexList 0 as bs a b where
157+ point = lens (\(x :> xs) -> (xs, x)) (\(xs, x') -> x' :> xs)
158+ {-# INLINE point #-}
59159
60−instance Splittable as bs cs => Splittable (a ': as) bs (a ': cs) where
61− split (a :> as)
62− = (a :> as', bs)
63− where (as', bs) = split as
160+instance
161+ ( IndexList (n - 1) as' bs' a b
162+ , as ~ (x ': as')
163+ , bs ~ (x ': bs')
164+ ) => IndexList n as bs a b where
165+ point = fromIso consing . alongside id (point @(n-1)) . second'
166+ {-# INLINE point #-}
64167
65168--------------------------------------------------------------------------------
66169-- * Convert tuples to/from HLists
67170
68171class ListTuple (tuple :: Type) (as :: [Type]) | as -> tuple where
69172 type ListToTuple as :: Type
173+ tupled :: Iso' (HList as) tuple
174+ tupled = iso listToTuple tupleToList
175+
70176 tupleToList :: tuple -> HList as
71177 listToTuple :: HList as -> tuple
72178
@@ -201,39 +307,23 @@ instance ListTuple (a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q, r, s) '[a, b
201307 listToTuple (a :> b :> c :> d :> e :> f :> g :> h :> j :> k :> l :> m :> n :> o :> p :> q :> r :> s :> Nil)
202308 = (a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q, r, s)
203309
204−--------------------------------------------------------------------------------
205−
206−class GCollectible (f :: Type -> Type) (as :: [Type]) | f -> as where
207− type GCollect f :: [Type]
208− gtoCollection :: f x -> HList as
209− gfromCollection :: HList as -> f x
210−
211−instance
212− ( GCollectible l as
213− , GCollectible r bs
214− , cs ~ (as ++ bs)
215− , Splittable as bs cs
216− ) => GCollectible (l :*: r) cs where
217− type GCollect (l :*: r) = GCollect l ++ GCollect r
218−
219− gtoCollection (l :*: r)
220− = gtoCollection l `append` gtoCollection r
221−
222− gfromCollection cs
223− = gfromCollection as :*: gfromCollection bs
224− where (as, bs) = split cs
225−
226−instance GCollectible (Rec0 a) '[a] where
227− type GCollect (Rec0 a) = '[a]
228− gtoCollection = (:> Nil) . unK1
229− gfromCollection = K1 . head'
230−
231−instance GCollectible U1 '[] where
232− type GCollect U1 = '[]
233− gtoCollection U1 = Nil
234− gfromCollection Nil = U1
235−
236−instance GCollectible f as => GCollectible (M1 m meta f) as where
237− type GCollect (M1 m meta f) = GCollect f
238− gtoCollection = gtoCollection . unM1
239− gfromCollection = M1 . gfromCollection
310+type family TupleToList a where
311+ TupleToList () = '[]
312+ TupleToList (a, b) = '[a, b]
313+ TupleToList (a, b, c) = '[a, b, c]
314+ TupleToList (a, b, c, d) = '[a, b, c, d]
315+ TupleToList (a, b, c, d, e) = '[a, b, c, d, e]
316+ TupleToList (a, b, c, d, e, f) = '[a, b, c, d, e, f]
317+ TupleToList (a, b, c, d, e, f, g) = '[a, b, c, d, e, f, g]
318+ TupleToList (a, b, c, d, e, f, g, h) = '[a, b, c, d, e, f, g, h]
319+ TupleToList (a, b, c, d, e, f, g, h, j) = '[a, b, c, d, e, f, g, h, j]
320+ TupleToList (a, b, c, d, e, f, g, h, j, k) = '[a, b, c, d, e, f, g, h, j, k]
321+ TupleToList (a, b, c, d, e, f, g, h, j, k, l) = '[a, b, c, d, e, f, g, h, j, k, l]
322+ TupleToList (a, b, c, d, e, f, g, h, j, k, l, m) = '[a, b, c, d, e, f, g, h, j, k, l, m]
323+ TupleToList (a, b, c, d, e, f, g, h, j, k, l, m, n) = '[a, b, c, d, e, f, g, h, j, k, l, m, n]
324+ TupleToList (a, b, c, d, e, f, g, h, j, k, l, m, n, o) = '[a, b, c, d, e, f, g, h, j, k, l, m, n, o]
325+ TupleToList (a, b, c, d, e, f, g, h, j, k, l, m, n, o, p) = '[a, b, c, d, e, f, g, h, j, k, l, m, n, o, p]
326+ TupleToList (a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q) = '[a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q]
327+ TupleToList (a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q, r) = '[a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q, r]
328+ TupleToList (a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q, r, s) = '[a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q, r, s]
329+ TupleToList a = '[a]
src/Data/Generics/Product/Internal/Positions.hs+47−31
@@ -1,5 +1,8 @@
1+{-# LANGUAGE TypeInType #-}
12{-# LANGUAGE AllowAmbiguousTypes #-}
3+{-# LANGUAGE ConstraintKinds #-}
24{-# LANGUAGE DataKinds #-}
5+{-# LANGUAGE FlexibleContexts #-}
36{-# LANGUAGE FlexibleInstances #-}
47{-# LANGUAGE FunctionalDependencies #-}
58{-# LANGUAGE KindSignatures #-}
@@ -14,7 +17,7 @@
1417-----------------------------------------------------------------------------
1518-- |
1619-- Module : Data.Generics.Product.Internal.Positions
17−-- Copyright : (C) 2017 Csongor Kiss
20+-- Copyright : (C) 2019 Csongor Kiss
1821-- License : BSD3
1922-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
2023-- Stability : experimental
@@ -25,46 +28,61 @@
2528-----------------------------------------------------------------------------
2629
2730module Data.Generics.Product.Internal.Positions
28− ( GHasPosition (..)
29− , type (<?)
31+ ( type (<?)
3032 , Size
33+ , CRep
3134 ) where
3235
33−import Data.Generics.Internal.Lens
34−
36+import Data.Generics.Internal.Families.Has (Pos)
3537import Data.Kind (Type)
3638import Data.Type.Bool (If, Not)
3739import GHC.Generics
3840import GHC.TypeLits (type (<=?), type (+), Nat)
3941
40−-- |As 'HasPosition' but over generic representations as defined by
41−-- "GHC.Generics".
42−class GHasPosition (offset :: Nat) (i :: Nat) (f :: Type -> Type) a | offset i f -> a where
43− gposition :: Lens' (f x) a
44−
45−instance GHasPosition i i (K1 R a) a where
46− gposition f (K1 x) = fmap K1 (f x)
47−
48−instance GHasPosition offset i f a => GHasPosition offset i (M1 m meta f) a where
49− gposition = mIso . gposition @offset @i
50−
51−instance
52− ( goLeft ~ (i <? (offset + Size l))
53− , offset' ~ (If goLeft offset (offset + Size l))
54− , GProductHasPosition offset' i l r a goLeft
55− ) => GHasPosition offset i (l :*: r) a where
56−
57− gposition = gproductPosition @offset' @i @_ @_ @_ @goLeft
42+-- | Alias for the kind of the generic rep
43+type G = Type -> Type
5844
45+--------------------------------------------------------------------------------
5946
60−class GProductHasPosition (offset :: Nat) (i :: Nat) l r a (left :: Bool) | offset i l r left -> a where
61− gproductPosition :: Lens' ((l :*: r) x) a
47+-- | In-order labeling of the generic tree with the field positions
48+--
49+-- We replace the (K1 R a) nodes with (K1 (Pos n) a), where 'n' is the position
50+-- of the field in question in the data type. This is convenient, because we
51+-- can reuse all the existing functions as long as they are polymorphic in the
52+-- first parameter of 'K1'.
53+type family CRep (a :: Type) :: G where
54+ CRep rep = Fst (Traverse (Rep rep) 1)
55+
56+-- | The actual traversal.
57+--
58+-- Might be cleaner if the sum and product parts were separated (as there's
59+-- and invariant that 'n' should be zero when we're at a sum node, which holds
60+-- for derived Generic instances (where the sums are strictly above the products))
61+type family Traverse (a :: G) (n :: Nat) :: (G, Nat) where
62+ Traverse (M1 mt m s) n
63+ = Traverse1 (M1 mt m) (Traverse s n)
64+ Traverse (l :+: r) n
65+ = '(Fst (Traverse l n) :+: Fst (Traverse r n), n)
66+ Traverse (l :*: r) n
67+ = TraverseProd (:*:) (Traverse l n) r
68+ Traverse (K1 _ p) n
69+ = '(K1 (Pos n) p, n + 1)
70+ Traverse U1 n
71+ = '(U1, n)
72+
73+type family Traverse1 (w :: G -> G) (z :: (G, Nat)) :: (G, Nat) where
74+ Traverse1 w '(i, n) = '(w i, n)
75+
76+-- | For products, we first traverse the left-hand side, followed by the second
77+-- using the counter returned by the left traversal.
78+type family TraverseProd (c :: G -> G -> G) (a :: (G, Nat)) (r :: G) :: (G, Nat) where
79+ TraverseProd w '(i, n) r = Traverse1 (w i) (Traverse r n)
6280
63−instance GHasPosition offset i l a => GProductHasPosition offset i l r a 'True where
64− gproductPosition = first . gposition @offset @i
81+--------------------------------------------------------------------------------
82+-- Utilities
6583
66−instance GHasPosition offset i r a => GProductHasPosition offset i l r a 'False where
67− gproductPosition = second . gposition @offset @i
84+type family Fst (p :: (a, b)) :: a where
85+ Fst '(a, b) = a
6886
6987type family Size f :: Nat where
7088 Size (l :*: r)
@@ -78,8 +96,6 @@ type family Size f :: Nat where
7896 Size f
7997 = 1
8098
81−--------------------------------------------------------------------------------
82−
8399type x <? y = Not (y <=? x)
84100infixl 4 <?
85101
src/Data/Generics/Product/Internal/Subtype.hs+15−10
@@ -1,3 +1,4 @@
1+{-# LANGUAGE TypeInType #-}
12{-# LANGUAGE AllowAmbiguousTypes #-}
23{-# LANGUAGE DataKinds #-}
34{-# LANGUAGE FlexibleContexts #-}
@@ -15,7 +16,7 @@
1516-----------------------------------------------------------------------------
1617-- |
1718-- Module : Data.Generics.Product.Internal.Subtype
18−-- Copyright : (C) 2017 Csongor Kiss
19+-- Copyright : (C) 2019 Csongor Kiss
1920-- License : BSD3
2021-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
2122-- Stability : experimental
@@ -31,11 +32,12 @@ module Data.Generics.Product.Internal.Subtype
3132 ) where
3233
3334import Data.Generics.Internal.Families
34−import Data.Generics.Internal.Lens
35−import Data.Generics.Product.Internal.Fields
35+import Data.Generics.Product.Internal.GLens
3636
3737import Data.Kind (Type)
3838import GHC.Generics
39+import GHC.TypeLits (Symbol)
40+import Data.Generics.Internal.Profunctor.Lens (view)
3941
4042--------------------------------------------------------------------------------
4143-- * Generic upcasting
@@ -47,10 +49,10 @@ instance (GUpcast sub a, GUpcast sub b) => GUpcast sub (a :*: b) where
4749 gupcast rep = gupcast rep :*: gupcast rep
4850
4951instance
50− GHasField field sub t
52+ GLens' (HasTotalFieldPSym field) sub t
5153 => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
5254
53− gupcast r = M1 (K1 (r ^. gfield @field))
55+ gupcast r = M1 (K1 (view (glens @(HasTotalFieldPSym field)) r))
5456
5557instance GUpcast sub sup => GUpcast sub (C1 c sup) where
5658 gupcast = M1 . gupcast
@@ -80,13 +82,16 @@ instance GSmash sub sup => GSmash (C1 c sub) sup where
8082instance GSmash sub sup => GSmash (D1 c sub) sup where
8183 gsmash sup (M1 sub) = M1 (gsmash sup sub)
8284
83−class GSmashLeaf sub sup (w :: Bool) where
85+class GSmashLeaf sub sup (w :: Maybe Type) where
8486 gsmashLeaf :: sup p -> sub p -> sub p
8587
8688instance
87− GHasField field sup t
88− => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'True where
89− gsmashLeaf sup _ = M1 (K1 (sup ^. gfield @field))
89+ GLens' (HasTotalFieldPSym field) sup t
90+ => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup ('Just t) where
91+ gsmashLeaf sup _ = M1 (K1 (view (glens @(HasTotalFieldPSym field)) sup))
9092
91−instance GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'False where
93+instance GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'Nothing where
9294 gsmashLeaf _ = id
95+
96+data HasTotalFieldPSym :: Symbol -> (TyFun (Type -> Type) (Maybe Type))
97+type instance Eval (HasTotalFieldPSym sym) tt = HasTotalFieldP sym tt
src/Data/Generics/Product/Internal/Typed.hsdeleted−62
@@ -1,62 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−
13−-----------------------------------------------------------------------------
14−-- |
15−-- Module : Data.Generics.Product.Internal.Typed
16−-- Copyright : (C) 2017 Csongor Kiss
17−-- License : BSD3
18−-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
19−-- Stability : experimental
20−-- Portability : non-portable
21−--
22−-- Derive record field getters and setters generically.
23−--
24−-----------------------------------------------------------------------------
25−
26−module Data.Generics.Product.Internal.Typed
27− ( GHasType (..)
28− ) where
29−
30−import Data.Generics.Internal.Families
31−import Data.Generics.Internal.Lens
32−
33−import Data.Kind (Type)
34−import GHC.Generics
35−
36−-- |As 'HasType' but over generic representations as defined by
37−-- "GHC.Generics".
38−class GHasType (f :: Type -> Type) a where
39− gtyped :: Lens' (f x) a
40−
41−instance GProductHasType l r a (HasTotalTypeP a l)
42− => GHasType (l :*: r) a where
43−
44− gtyped = gproductTyped @_ @_ @_ @(HasTotalTypeP a l)
45−
46−instance (GHasType l a, GHasType r a) => GHasType (l :+: r) a where
47− gtyped = combine (gtyped @l) (gtyped @r)
48−
49−instance GHasType (K1 R a) a where
50− gtyped f (K1 x) = fmap K1 (f x)
51−
52−instance GHasType f a => GHasType (M1 m meta f) a where
53− gtyped = mIso . gtyped
54−
55−class GProductHasType l r a (contains :: Bool) where
56− gproductTyped :: Lens' ((l :*: r) x) a
57−
58−instance GHasType l a => GProductHasType l r a 'True where
59− gproductTyped = first . gtyped
60−
61−instance GHasType r a => GProductHasType l r a 'False where
62− gproductTyped = second . gtyped
src/Data/Generics/Product/Param.hsnew file+121
@@ -0,0 +1,121 @@
1+{-# LANGUAGE PolyKinds #-}
2+{-# LANGUAGE TypeFamilies #-}
3+{-# LANGUAGE AllowAmbiguousTypes #-}
4+{-# LANGUAGE DataKinds #-}
5+{-# LANGUAGE FlexibleContexts #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE FunctionalDependencies #-}
8+{-# LANGUAGE GADTs #-}
9+{-# LANGUAGE KindSignatures #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TypeApplications #-}
12+{-# LANGUAGE TypeOperators #-}
13+{-# LANGUAGE UndecidableInstances #-}
14+
15+--------------------------------------------------------------------------------
16+-- |
17+-- Module : Data.Generics.Product.Param
18+-- Copyright : (C) 2019 Csongor Kiss
19+-- License : BSD3
20+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21+-- Stability : experimental
22+-- Portability : non-portable
23+--
24+-- Derive traversals over type parameters
25+--
26+--------------------------------------------------------------------------------
27+
28+module Data.Generics.Product.Param
29+ ( Rec (Rec) -- TODO: this has to be re-exported so the constructor is visible for Coercible... is there a better way?
30+ , HasParam (..)
31+ ) where
32+
33+import GHC.TypeLits
34+import Data.Generics.Internal.Void
35+import Data.Generics.Internal.Families.Changing
36+import Data.Generics.Internal.VL.Traversal
37+
38+import GHC.Generics
39+import Data.Kind
40+import Data.Generics.Internal.VL.Iso
41+import Data.Generics.Internal.GenericN
42+import Data.Generics.Internal.Errors
43+
44+class HasParam (p :: Nat) s t a b | p t a -> s, p s b -> t, p s -> a, p t -> b where
45+ param :: Applicative g => (a -> g b) -> s -> g t
46+
47+instance
48+ ( GenericN s
49+ , GenericN t
50+ -- TODO: merge the old 'Changing' code with 'GenericN'
51+ , Defined (Rep s)
52+ (NoGeneric s
53+ '[ 'Text "arising from a generic traversal of the type parameter at position " ':<>: QuoteType n
54+ , 'Text "of type " ':<>: QuoteType a ':<>: 'Text " in " ':<>: QuoteType s
55+ ])
56+ (() :: Constraint)
57+ , s ~ Infer t (P n b 'PTag) a
58+ , t ~ Infer s (P n a 'PTag) b
59+ , Error ((ArgCount s) <=? n) n (ArgCount s) s
60+ , a ~ ArgAt s n
61+ , b ~ ArgAt t n
62+ , GHasParam n (RepN s) (RepN t) a b
63+ ) => HasParam n s t a b where
64+
65+ param = confusing (\f s -> toN <$> gparam @n f (fromN s))
66+ {-# INLINE param #-}
67+
68+type family Error (b :: Bool) (expected :: Nat) (actual :: Nat) (s :: Type) :: Constraint where
69+ Error 'False _ _ _
70+ = ()
71+
72+ Error 'True expected actual typ
73+ = TypeError
74+ ( 'Text "Expected a type with at least "
75+ ':<>: 'ShowType (expected + 1)
76+ ':<>: 'Text " parameters, but "
77+ ':$$: 'ShowType typ
78+ ':<>: 'Text " only has "
79+ ':<>: 'ShowType actual
80+ )
81+
82+-- TODO [1.0.0.0]: none of this is needed.
83+
84+instance {-# OVERLAPPING #-} HasParam p (Void1 a) (Void1 b) a b where
85+ param = undefined
86+
87+class GHasParam (p :: Nat) s t a b where
88+ gparam :: forall g (x :: Type). Applicative g => (a -> g b) -> s x -> g (t x)
89+
90+instance (GHasParam p l l' a b, GHasParam p r r' a b) => GHasParam p (l :*: r) (l' :*: r') a b where
91+ gparam f (l :*: r) = (:*:) <$> gparam @p f l <*> gparam @p f r
92+
93+instance (GHasParam p l l' a b, GHasParam p r r' a b) => GHasParam p (l :+: r) (l' :+: r') a b where
94+ gparam f (L1 l) = L1 <$> gparam @p f l
95+ gparam f (R1 r) = R1 <$> gparam @p f r
96+
97+instance GHasParam p U1 U1 a b where
98+ gparam _ _ = pure U1
99+
100+instance GHasParam p s t a b => GHasParam p (M1 m meta s) (M1 m meta t) a b where
101+ gparam f (M1 x) = M1 <$> gparam @p f x
102+
103+-- the parameter we're looking for
104+instance GHasParam p (Rec (param p) a) (Rec (param p) b) a b where
105+ gparam = recIso
106+
107+-- other recursion
108+instance {-# OVERLAPPABLE #-}
109+ ( GHasParamRec (LookupParam si p) s t a b
110+ -- TODO: reindex `ti`
111+ ) => GHasParam p (Rec si s) (Rec ti t) a b where
112+ gparam f (Rec (K1 x)) = Rec . K1 <$> gparamRec @(LookupParam si p) f x
113+
114+class GHasParamRec (param :: Maybe Nat) s t a b | param t a b -> s, param s a b -> t where
115+ gparamRec :: forall g. Applicative g => (a -> g b) -> s -> g t
116+
117+instance GHasParamRec 'Nothing a a c d where
118+ gparamRec _ = pure
119+
120+instance (HasParam n s t a b) => GHasParamRec ('Just n) s t a b where
121+ gparamRec = param @n
src/Data/Generics/Product/Positions.hs+165−41
@@ -1,5 +1,10 @@
1+{-# LANGUAGE TypeInType #-}
2+{-# LANGUAGE PartialTypeSignatures #-}
3+{-# LANGUAGE CPP #-}
14{-# LANGUAGE AllowAmbiguousTypes #-}
5+{-# LANGUAGE ConstraintKinds #-}
26{-# LANGUAGE DataKinds #-}
7+{-# LANGUAGE FlexibleContexts #-}
38{-# LANGUAGE FlexibleInstances #-}
49{-# LANGUAGE FunctionalDependencies #-}
510{-# LANGUAGE KindSignatures #-}
@@ -14,7 +19,7 @@
1419-----------------------------------------------------------------------------
1520-- |
1621-- Module : Data.Generics.Product.Positions
17−-- Copyright : (C) 2017 Csongor Kiss
22+-- Copyright : (C) 2019 Csongor Kiss
1823-- License : BSD3
1924-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
2025-- Stability : experimental
@@ -27,73 +32,189 @@
2732module Data.Generics.Product.Positions
2833 ( -- *Lenses
2934
30− -- $example
35+ -- $setup
3136 HasPosition (..)
37+ , HasPosition' (..)
38+ , HasPosition_ (..)
39+ , HasPosition0 (..)
40+
41+ , getPosition
42+ , setPosition
3243 ) where
3344
34−import Data.Generics.Internal.Lens
45+import Data.Generics.Internal.VL.Lens as VL
46+import Data.Generics.Internal.Void
47+import Data.Generics.Internal.Families
3548import Data.Generics.Product.Internal.Positions
49+import Data.Generics.Product.Internal.GLens
50+import Data.Generics.Internal.Errors
3651
3752import Data.Kind (Constraint, Type)
3853import Data.Type.Bool (type (&&))
3954import GHC.Generics
4055import GHC.TypeLits (type (<=?), Nat, TypeError, ErrorMessage(..))
56+import Data.Generics.Internal.Profunctor.Lens as P
57+import Data.Coerce
4158
42−-- $example
43−-- @
44−-- module Example where
45−--
46−-- import Data.Generics.Product
47−-- import GHC.Generics
59+-- $setup
60+-- == /Running example:/
4861--
49−-- data Human = Human
50−-- { name :: String
51−-- , age :: Int
52−-- , address :: String
53−-- }
54−-- deriving (Generic, Show)
55−--
56−-- human :: Human
57−-- human = Human \"Tunyasz\" 50 \"London\"
58−-- @
62+-- >>> :set -XTypeApplications
63+-- >>> :set -XDataKinds
64+-- >>> :set -XDeriveGeneric
65+-- >>> :set -XGADTs
66+-- >>> :set -XFlexibleContexts
67+-- >>> import GHC.Generics
68+-- >>> :m +Data.Generics.Internal.VL.Lens
69+-- >>> :m +Data.Function
70+-- >>> :{
71+-- data Human = Human
72+-- { name :: String
73+-- , age :: Int
74+-- , address :: String
75+-- }
76+-- deriving (Generic, Show)
77+-- human :: Human
78+-- human = Human "Tunyasz" 50 "London"
79+-- :}
5980
6081-- |Records that have a field at a given position.
61−class HasPosition (i :: Nat) a s | s i -> a where
82+class HasPosition (i :: Nat) s t a b | s i -> a, t i -> b, s i b -> t, t i a -> s where
6283 -- |A lens that focuses on a field at a given position. Compatible with the
6384 -- lens package's 'Control.Lens.Lens' type.
6485 --
6586 -- >>> human ^. position @1
6687 -- "Tunyasz"
67− -- >>> human & position @2 .~ "Berlin"
88+ -- >>> human & position @3 .~ "Berlin"
6889 -- Human {name = "Tunyasz", age = 50, address = "Berlin"}
69− position :: Lens' s a
70− position f s
71− = fmap (flip (setPosition @i) s) (f (getPosition @i s))
72− -- = fmap (setPosition s) (f (getPosition s))
73−
74− -- |Get positional field
7590 --
76− -- >>> getPosition @1 human
77− -- "Tunyasz"
78− getPosition :: s -> a
79− getPosition s = s ^. position @i
80−
81− -- |Set positional field
91+ -- === /Type errors/
8292 --
83− -- >>> setPosition @2 (setField @1 "Tamas" human) 30
84− -- Human "Tamas" 30 "London"
85− setPosition :: a -> s -> s
86− setPosition = set (position @i)
93+ -- >>> human & position @4 .~ "Berlin"
94+ -- ...
95+ -- ... The type Human does not contain a field at position 4
96+ -- ...
97+ position :: VL.Lens s t a b
98+
99+class HasPosition_ (i :: Nat) s t a b where
100+ position_ :: VL.Lens s t a b
101+
102+-- |Records that have a field at a given position.
103+--
104+-- The difference between 'HasPosition' and 'HasPosition_' is similar to the
105+-- one between 'Data.Generics.Product.Fields.HasField' and
106+-- 'Data.Generics.Product.Fields.HasField_'.
107+-- See 'Data.Generics.Product.Fields.HasField_'.
108+class HasPosition' (i :: Nat) s a | s i -> a where
109+ position' :: VL.Lens s s a a
87110
88− {-# MINIMAL position | setPosition, getPosition #-}
111+-- |Records that have a field at a given position.
112+--
113+-- This class gives the minimal constraints needed to define this lens.
114+-- For common uses, see 'HasPosition'.
115+class HasPosition0 (i :: Nat) s t a b where
116+ position0 :: VL.Lens s t a b
117+
118+-- |
119+-- >>> getPosition @2 human
120+-- 50
121+getPosition :: forall i s a. HasPosition' i s a => s -> a
122+getPosition s = s ^. position' @i
123+
124+-- |
125+-- >>> setPosition @2 60 human
126+-- Human {name = "Tunyasz", age = 60, address = "London"}
127+setPosition :: forall i s a. HasPosition' i s a => a -> s -> s
128+setPosition = VL.set (position' @i)
89129
90130instance
91131 ( Generic s
92132 , ErrorUnless i s (0 <? i && i <=? Size (Rep s))
93− , GHasPosition 1 i (Rep s) a
94− ) => HasPosition i a s where
133+ , cs ~ CRep s
134+ , Coercible (Rep s) cs
135+ , GLens' (HasTotalPositionPSym i) cs a
136+ , Defined (Rep s)
137+ (NoGeneric s '[ 'Text "arising from a generic lens focusing on the field at"
138+ , 'Text "position " ':<>: QuoteType i ':<>: 'Text " of type " ':<>: QuoteType a
139+ ':<>: 'Text " in " ':<>: QuoteType s
140+ ])
141+ (() :: Constraint)
142+ ) => HasPosition' i s a where
143+ position' f s = VL.ravel (repLens . coerced @cs @cs . glens @(HasTotalPositionPSym i)) f s
144+ {-# INLINE position' #-}
145+
146+-- this is to 'hide' the equality constraints which interfere with inlining
147+-- pre 8.4.3
148+class (~~) (a :: k) (b :: k) | a -> b, b -> a
149+instance (a ~ b) => (~~) a b
150+
151+instance -- see Note [Changing type parameters]
152+ ( ErrorUnless i s (0 <? i && i <=? Size (Rep s))
153+ , HasTotalPositionP i (CRep s) ~~ 'Just a
154+ , HasTotalPositionP i (CRep t) ~~ 'Just b
155+ , HasTotalPositionP i (CRep (Indexed s)) ~~ 'Just a'
156+ , HasTotalPositionP i (CRep (Indexed t)) ~~ 'Just b'
157+ , t ~~ Infer s a' b
158+ , s ~~ Infer t b' a
159+ , Coercible (CRep s) (Rep s)
160+ , Coercible (CRep t) (Rep t)
161+ , HasPosition0 i s t a b
162+ ) => HasPosition i s t a b where
163+
164+ position = position0 @i
165+ {-# INLINE position #-}
95166
96− position = ravel (repIso . gposition @1 @i)
167+-- We wouldn't need the universal 'x' here if we could express above that
168+-- forall x. Coercible (cs x) (Rep s x), but this requires quantified
169+-- constraints
170+coerced :: forall s t s' t' x a b. (Coercible t t', Coercible s s')
171+ => P.ALens a b (s x) (t x) -> P.ALens a b (s' x) (t' x)
172+coerced = coerce
173+{-# INLINE coerced #-}
174+
175+-- | See Note [Uncluttering type signatures]
176+#if __GLASGOW_HASKELL__ < 804
177+-- >>> :t position
178+-- position
179+-- :: (HasPosition i s t a b, Functor f) => (a -> f b) -> s -> f t
180+#else
181+-- >>> :t position
182+-- position
183+-- :: (Functor f, HasPosition i s t a b) => (a -> f b) -> s -> f t
184+#endif
185+instance {-# OVERLAPPING #-} HasPosition f (Void1 a) (Void1 b) a b where
186+ position = undefined
187+
188+instance
189+ ( ErrorUnless i s (0 <? i && i <=? Size (Rep s))
190+ , UnifyHead s t
191+ , UnifyHead t s
192+ , Coercible (CRep s) (Rep s)
193+ , Coercible (CRep t) (Rep t)
194+ , HasPosition0 i s t a b
195+ ) => HasPosition_ i s t a b where
196+
197+ position_ = position0 @i
198+ {-# INLINE position_ #-}
199+
200+instance {-# OVERLAPPING #-} HasPosition_ f (Void1 a) (Void1 b) a b where
201+ position_ = undefined
202+
203+instance
204+ ( Generic s
205+ , Generic t
206+ , GLens (HasTotalPositionPSym i) (CRep s) (CRep t) a b
207+ , Coercible (CRep s) (Rep s)
208+ , Coercible (CRep t) (Rep t)
209+ , Defined (Rep s)
210+ (NoGeneric s '[ 'Text "arising from a generic lens focusing on the field at"
211+ , 'Text "position " ':<>: QuoteType i ':<>: 'Text " of type " ':<>: QuoteType a
212+ ':<>: 'Text " in " ':<>: QuoteType s
213+ ])
214+ (() :: Constraint)
215+ ) => HasPosition0 i s t a b where
216+ position0 = VL.ravel (repLens . coerced @(CRep s) @(CRep t) . glens @(HasTotalPositionPSym i))
217+ {-# INLINE position0 #-}
97218
98219type family ErrorUnless (i :: Nat) (s :: Type) (hasP :: Bool) :: Constraint where
99220 ErrorUnless i s 'False
@@ -106,3 +227,6 @@ type family ErrorUnless (i :: Nat) (s :: Type) (hasP :: Bool) :: Constraint wher
106227
107228 ErrorUnless _ _ 'True
108229 = ()
230+
231+data HasTotalPositionPSym :: Nat -> (TyFun (Type -> Type) (Maybe Type))
232+type instance Eval (HasTotalPositionPSym t) tt = HasTotalPositionP t tt
src/Data/Generics/Product/Subtype.hs+106−30
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE AllowAmbiguousTypes #-}
23{-# LANGUAGE DataKinds #-}
34{-# LANGUAGE FlexibleContexts #-}
@@ -15,7 +16,7 @@
1516-----------------------------------------------------------------------------
1617-- |
1718-- Module : Data.Generics.Product.Subtype
18−-- Copyright : (C) 2017 Csongor Kiss
19+-- Copyright : (C) 2019 Csongor Kiss
1920-- License : BSD3
2021-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
2122-- Stability : experimental
@@ -28,36 +29,45 @@
2829module Data.Generics.Product.Subtype
2930 ( -- *Lenses
3031 --
31− -- $example
32+ -- $setup
3233 Subtype (..)
3334 ) where
3435
35−import Data.Generics.Internal.Lens
36+import Data.Generics.Internal.Families
37+import Data.Generics.Internal.VL.Lens as VL
38+import Data.Generics.Internal.Void
3639import Data.Generics.Product.Internal.Subtype
3740
3841import GHC.Generics (Generic (Rep, to, from) )
42+import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
43+import Data.Kind (Type, Constraint)
44+import Data.Generics.Internal.Profunctor.Lens hiding (set)
45+import Data.Generics.Internal.Errors
3946
40−-- $example
41−-- @
42−-- module Example where
47+-- $setup
48+-- == /Running example:/
4349--
44−-- import Data.Generics.Product
45−-- import GHC.Generics
46−--
47−-- data Human = Human
48−-- { name :: String
49−-- , age :: Int
50−-- , address :: String
51−-- } deriving (Generic, Show)
52−--
53−-- data Animal = Animal
54−-- { name :: String
55−-- , age :: Int
56−-- } deriving (Generic, Show)
57−--
58−-- human :: Human
59−-- human = Human \"Tunyasz\" 50 \"London\"
60−-- @
50+-- >>> :set -XTypeApplications
51+-- >>> :set -XDataKinds
52+-- >>> :set -XDeriveGeneric
53+-- >>> :set -XDuplicateRecordFields
54+-- >>> import GHC.Generics
55+-- >>> :m +Data.Generics.Internal.VL.Lens
56+-- >>> :{
57+-- data Human = Human
58+-- { name :: String
59+-- , age :: Int
60+-- , address :: String
61+-- }
62+-- deriving (Generic, Show)
63+-- data Animal = Animal
64+-- { name :: String
65+-- , age :: Int
66+-- }
67+-- deriving (Generic, Show)
68+-- human :: Human
69+-- human = Human "Tunyasz" 50 "London"
70+-- :}
6171
6272-- |Structural subtype relationship
6373--
@@ -73,15 +83,21 @@ class Subtype sup sub where
7383 --
7484 -- >>> set (super @Animal) (Animal "dog" 10) human
7585 -- Human {name = "dog", age = 10, address = "London"}
76− super :: Lens' sub sup
77− super f sub
78− = fmap (`smash` sub) (f (upcast sub))
86+ super :: VL.Lens sub sub sup sup
87+ super
88+ = VL.lens upcast (uncurry smash . swap)
7989
8090 -- |Cast the more specific subtype to the more general supertype
8191 --
8292 -- >>> upcast human :: Animal
8393 -- Animal {name = "Tunyasz", age = 50}
8494 --
95+ -- >>> upcast (upcast human :: Animal) :: Human
96+ -- ...
97+ -- ... The type 'Animal' is not a subtype of 'Human'.
98+ -- ... The following fields are missing from 'Animal':
99+ -- ... address
100+ -- ...
85101 upcast :: sub -> sup
86102 upcast s = s ^. super @sup
87103
@@ -90,15 +106,75 @@ class Subtype sup sub where
90106 -- >>> smash (Animal "dog" 10) human
91107 -- Human {name = "dog", age = 10, address = "London"}
92108 smash :: sup -> sub -> sub
93− smash = set (super @sup)
109+ smash = VL.set (super @sup)
94110
95111 {-# MINIMAL super | smash, upcast #-}
96112
97113instance
98− ( GSmash (Rep a) (Rep b)
99− , GUpcast (Rep a) (Rep b)
100− , Generic a
114+ ( Generic a
101115 , Generic b
116+ , GSmash (Rep a) (Rep b)
117+ , GUpcast (Rep a) (Rep b)
118+ , CustomError a b
102119 ) => Subtype b a where
103120 smash p b = to $ gsmash (from p) (from b)
104121 upcast = to . gupcast . from
122+
123+type family CustomError a b :: Constraint where
124+ CustomError a b =
125+ ( ErrorUnless b a (CollectFieldsOrdered (Rep b) \\ CollectFieldsOrdered (Rep a))
126+ , Defined (Rep a)
127+ (NoGeneric a '[ 'Text "arising from a generic lens focusing on " ':<>: QuoteType b
128+ , 'Text "as a supertype of " ':<>: QuoteType a
129+ ])
130+ (() :: Constraint)
131+ , Defined (Rep b)
132+ (NoGeneric b '[ 'Text "arising from a generic lens focusing on " ':<>: QuoteType b
133+ , 'Text "as a supertype of " ':<>: QuoteType a
134+ ])
135+ (() :: Constraint)
136+ )
137+
138+instance {-# OVERLAPPING #-} Subtype a a where
139+ super = id
140+
141+-- | See Note [Uncluttering type signatures]
142+#if __GLASGOW_HASKELL__ < 804
143+-- >>> :t super
144+-- super
145+-- :: (Subtype sup sub, Functor f) => (sup -> f sup) -> sub -> f sub
146+#else
147+-- >>> :t super
148+-- super
149+-- :: (Functor f, Subtype sup sub) => (sup -> f sup) -> sub -> f sub
150+#endif
151+instance {-# OVERLAPPING #-} Subtype a Void where
152+ super = undefined
153+
154+-- | See Note [Uncluttering type signatures]
155+#if __GLASGOW_HASKELL__ < 804
156+-- >>> :t super @Int
157+-- super @Int
158+-- :: (Subtype Int sub, Functor f) => (Int -> f Int) -> sub -> f sub
159+#else
160+-- >>> :t super @Int
161+-- super @Int
162+-- :: (Functor f, Subtype Int sub) => (Int -> f Int) -> sub -> f sub
163+#endif
164+instance {-# OVERLAPPING #-} Subtype Void a where
165+ super = undefined
166+
167+type family ErrorUnless (sup :: Type) (sub :: Type) (diff :: [Symbol]) :: Constraint where
168+ ErrorUnless _ _ '[]
169+ = ()
170+
171+ ErrorUnless sup sub fs
172+ = TypeError
173+ ( 'Text "The type '"
174+ ':<>: 'ShowType sub
175+ ':<>: 'Text "' is not a subtype of '"
176+ ':<>: 'ShowType sup ':<>: 'Text "'."
177+ ':$$: 'Text "The following fields are missing from '"
178+ ':<>: 'ShowType sub ':<>: 'Text "':"
179+ ':$$: ShowSymbols fs
180+ )
src/Data/Generics/Product/Typed.hs+105−34
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE AllowAmbiguousTypes #-}
23{-# LANGUAGE DataKinds #-}
34{-# LANGUAGE FlexibleContexts #-}
@@ -7,54 +8,66 @@
78{-# LANGUAGE ScopedTypeVariables #-}
89{-# LANGUAGE TypeApplications #-}
910{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeInType #-}
1012{-# LANGUAGE TypeOperators #-}
1113{-# LANGUAGE UndecidableInstances #-}
1214
1315-----------------------------------------------------------------------------
1416-- |
1517-- Module : Data.Generics.Product.Typed
16−-- Copyright : (C) 2017 Csongor Kiss
18+-- Copyright : (C) 2019 Csongor Kiss
1719-- License : BSD3
1820-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1921-- Stability : experimental
2022-- Portability : non-portable
2123--
22−-- Derive record field getters and setters generically.
24+-- Derive lenses of a given type in a product.
2325--
2426-----------------------------------------------------------------------------
2527
2628module Data.Generics.Product.Typed
2729 ( -- *Lenses
2830 --
29− -- $example
31+ -- $setup
3032 HasType (..)
3133 ) where
3234
3335import Data.Generics.Internal.Families
34−import Data.Generics.Internal.Lens
35−import Data.Generics.Product.Internal.Typed
36+import Data.Generics.Internal.VL.Lens as VL
37+import Data.Generics.Internal.Void
38+import Data.Generics.Product.Internal.GLens
3639
3740import Data.Kind (Constraint, Type)
3841import GHC.Generics (Generic (Rep))
3942import GHC.TypeLits (TypeError, ErrorMessage (..))
43+import Data.Generics.Internal.Profunctor.Lens
44+import Data.Generics.Internal.Errors
4045
41−-- $example
42−-- @
43−-- module Example where
46+-- $setup
47+-- == /Running example:/
4448--
45−-- import Data.Generics.Product
46−-- import GHC.Generics
47−--
48−-- data Human = Human
49−-- { name :: String
50−-- , age :: Int
51−-- , address :: String
52−-- }
53−-- deriving (Generic, Show)
54−--
55−-- human :: Human
56−-- human = Human \"Tunyasz\" 50 \"London\"
57−-- @
49+-- >>> :set -XTypeApplications
50+-- >>> :set -XDataKinds
51+-- >>> :set -XDeriveGeneric
52+-- >>> import GHC.Generics
53+-- >>> :m +Data.Generics.Internal.VL.Lens
54+-- >>> :{
55+-- data Human
56+-- = Human
57+-- { name :: String
58+-- , age :: Int
59+-- , address :: String
60+-- , tall :: Bool
61+-- }
62+-- | HumanNoTall
63+-- { name :: String
64+-- , age :: Int
65+-- , address :: String
66+-- }
67+-- deriving (Generic, Show)
68+-- human :: Human
69+-- human = Human "Tunyasz" 50 "London" False
70+-- :}
5871
5972-- |Records that have a field with a unique type.
6073class HasType a s where
@@ -63,9 +76,29 @@ class HasType a s where
6376 --
6477 -- >>> human ^. typed @Int
6578 -- 50
66− typed :: Lens' s a
67− typed f t
68− = fmap (flip (setTyped @a) t) (f (getTyped @a t))
79+ --
80+ -- === /Type errors/
81+ --
82+ -- >>> human ^. typed @String
83+ -- ...
84+ -- ...
85+ -- ... The type Human contains multiple values of type [Char].
86+ -- ... The choice of value is thus ambiguous. The offending constructors are:
87+ -- ... Human
88+ -- ... HumanNoTall
89+ -- ...
90+ --
91+ -- >>> human ^. typed @Bool
92+ -- ...
93+ -- ...
94+ -- ... Not all constructors of the type Human contain a field of type Bool.
95+ -- ... The offending constructors are:
96+ -- ... HumanNoTall
97+ -- ...
98+ typed :: VL.Lens s s a a
99+ typed
100+ = VL.lens (getTyped @a) (uncurry (setTyped @a) . swap)
101+ {-# INLINE typed #-}
69102
70103 -- |Get field at type.
71104 getTyped :: s -> a
@@ -73,20 +106,43 @@ class HasType a s where
73106
74107 -- |Set field at type.
75108 setTyped :: a -> s -> s
76− setTyped = set (typed @a)
109+ setTyped = VL.set (typed @a)
77110
78111 {-# MINIMAL typed | setTyped, getTyped #-}
79112
80113instance
81114 ( Generic s
82− , ErrorUnlessOne a s (CountTotalType a (Rep s))
83− , GHasType (Rep s) a
115+ , ErrorUnlessOne a s (CollectTotalType a (Rep s))
116+ , Defined (Rep s)
117+ (NoGeneric s '[ 'Text "arising from a generic lens focusing on a field of type " ':<>: QuoteType a])
118+ (() :: Constraint)
119+ , GLens (HasTotalTypePSym a) (Rep s) (Rep s) a a
84120 ) => HasType a s where
85121
86− typed = ravel (repIso . gtyped)
122+ typed f s = VL.ravel (repLens . glens @(HasTotalTypePSym a)) f s
123+
124+instance {-# OVERLAPPING #-} HasType a a where
125+ getTyped = id
126+ {-# INLINE getTyped #-}
127+
128+ setTyped a _ = a
129+ {-# INLINE setTyped #-}
130+
131+-- | See Note [Uncluttering type signatures]
132+#if __GLASGOW_HASKELL__ < 804
133+-- >>> :t typed
134+-- typed :: (HasType a s, Functor f) => (a -> f a) -> s -> f s
135+#else
136+-- >>> :t typed
137+-- typed :: (Functor f, HasType a s) => (a -> f a) -> s -> f s
138+#endif
139+--
140+-- Note that this might not longer be needed given the above 'HasType a a' instance.
141+instance {-# OVERLAPPING #-} HasType a Void where
142+ typed = undefined
87143
88−type family ErrorUnlessOne (a :: Type) (s :: Type) (count :: Count) :: Constraint where
89− ErrorUnlessOne a s 'None
144+type family ErrorUnlessOne (a :: Type) (s :: Type) (stat :: TypeStat) :: Constraint where
145+ ErrorUnlessOne a s ('TypeStat '[_] '[] '[])
90146 = TypeError
91147 ( 'Text "The type "
92148 ':<>: 'ShowType s
@@ -94,14 +150,29 @@ type family ErrorUnlessOne (a :: Type) (s :: Type) (count :: Count) :: Constrain
94150 ':<>: 'ShowType a
95151 )
96152
97− ErrorUnlessOne a s 'Multiple
153+ ErrorUnlessOne a s ('TypeStat (n ': ns) _ _)
154+ = TypeError
155+ ( 'Text "Not all constructors of the type "
156+ ':<>: 'ShowType s
157+ ':<>: 'Text " contain a field of type "
158+ ':<>: 'ShowType a ':<>: 'Text "."
159+ ':$$: 'Text "The offending constructors are:"
160+ ':$$: ShowSymbols (n ': ns)
161+ )
162+
163+ ErrorUnlessOne a s ('TypeStat _ (m ': ms) _)
98164 = TypeError
99165 ( 'Text "The type "
100166 ':<>: 'ShowType s
101167 ':<>: 'Text " contains multiple values of type "
102− ':<>: 'ShowType a
103− ':<>: 'Text "; the choice of value is thus ambiguous"
168+ ':<>: 'ShowType a ':<>: 'Text "."
169+ ':$$: 'Text "The choice of value is thus ambiguous. The offending constructors are:"
170+ ':$$: ShowSymbols (m ': ms)
104171 )
105172
106− ErrorUnlessOne _ _ 'One
173+ ErrorUnlessOne _ _ ('TypeStat '[] '[] _)
107174 = ()
175+
176+data HasTotalTypePSym :: Type -> (TyFun (Type -> Type) (Maybe Type))
177+type instance Eval (HasTotalTypePSym t) tt = HasTotalTypeP t tt
178+
src/Data/Generics/Product/Types.hsnew file+367
@@ -0,0 +1,367 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE DefaultSignatures #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE KindSignatures #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE Rank2Types #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TypeApplications #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE TypeOperators #-}
14+{-# LANGUAGE UndecidableInstances #-}
15+
16+-----------------------------------------------------------------------------
17+-- |
18+-- Module : Data.Generics.Product.Types
19+-- Copyright : (C) 2019 Csongor Kiss
20+-- License : BSD3
21+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
22+-- Stability : experimental
23+-- Portability : non-portable
24+--
25+-- Derive traversals of a given type in a product.
26+--
27+-----------------------------------------------------------------------------
28+
29+module Data.Generics.Product.Types
30+ ( -- *Traversals
31+ --
32+ -- $setup
33+ HasTypes
34+ , types
35+
36+ -- * Custom traversal strategies
37+ -- $custom
38+ , Children
39+ , ChGeneric
40+
41+ , HasTypesUsing
42+ , typesUsing
43+
44+ , HasTypesCustom (typesCustom)
45+
46+ ) where
47+
48+import Data.Kind
49+import Data.Int (Int8, Int16, Int32, Int64)
50+import Data.Word (Word, Word8, Word16, Word32, Word64)
51+import qualified Data.Text as T
52+
53+import GHC.Generics
54+import GHC.TypeLits
55+import Data.Generics.Internal.VL.Traversal
56+import Data.Generics.Internal.Errors
57+
58+-- $setup
59+-- == /Running example:/
60+--
61+-- >>> :set -XTypeApplications
62+-- >>> :set -XDeriveGeneric
63+-- >>> :set -XScopedTypeVariables
64+-- >>> import GHC.Generics
65+-- >>> :m +Data.Generics.Internal.VL.Traversal
66+-- >>> :m +Data.Generics.Internal.VL.Lens
67+-- >>> :{
68+-- data WTree a w
69+-- = Leaf a
70+-- | Fork (WTree a w) (WTree a w)
71+-- | WithWeight (WTree a w) w
72+-- deriving (Generic, Show)
73+-- :}
74+
75+--------------------------------------------------------------------------------
76+-- HasTypes
77+--------------------------------------------------------------------------------
78+
79+-- TODO [1.0.0.0]: use type-changing variant internally
80+
81+-- | Traverse all types in the given structure.
82+--
83+-- For example, to update all 'String's in a @WTree (Maybe String) String@, we can write
84+--
85+-- >>> myTree = WithWeight (Fork (Leaf (Just "hello")) (Leaf Nothing)) "world"
86+-- >>> over (types @String) (++ "!") myTree
87+-- WithWeight (Fork (Leaf (Just "hello!")) (Leaf Nothing)) "world!"
88+--
89+-- The traversal is /deep/, which means that not just the immediate
90+-- children are visited, but all nested values too.
91+types :: forall a s. HasTypes s a => Traversal' s a
92+types = types_ @s @a
93+{-# INLINE types #-}
94+
95+class HasTypes s a where
96+ types_ :: Traversal' s a
97+
98+ default types_ :: Traversal' s a
99+ types_ _ = pure
100+ {-# INLINE types_ #-}
101+
102+instance
103+ ( HasTypesUsing ChGeneric s a
104+ ) => HasTypes s a where
105+ types_ = typesUsing_ @ChGeneric
106+ {-# INLINE types_ #-}
107+
108+--------------------------------------------------------------------------------
109+data Void
110+instance {-# OVERLAPPING #-} HasTypes Void a where
111+ types_ _ = pure
112+
113+instance {-# OVERLAPPING #-} HasTypes s Void where
114+ types_ _ = pure
115+
116+instance {-# OVERLAPPING #-} HasTypesUsing ch Void a where
117+ typesUsing_ _ = pure
118+
119+instance {-# OVERLAPPING #-} HasTypesUsing ch s Void where
120+ typesUsing_ _ = pure
121+--------------------------------------------------------------------------------
122+
123+--------------------------------------------------------------------------------
124+-- HasTypesUsing
125+--------------------------------------------------------------------------------
126+
127+-- $custom
128+--
129+-- The default traversal strategy 'types' recurses into each node of the type
130+-- using the 'Generic' instance for the nodes. However, in general not all
131+-- nodes will have a 'Generic' instance. For example:
132+--
133+-- >>> data Opaque = Opaque String deriving Show
134+-- >>> myTree = WithWeight (Fork (Leaf (Opaque "foo")) (Leaf (Opaque "bar"))) False
135+-- >>> over (types @String) (++ "!") myTree
136+-- ...
137+-- ... | No instance for ‘Generic Opaque’
138+-- ... | arising from a generic traversal.
139+-- ... | Either derive the instance, or define a custom traversal using ‘HasTypesCustom’
140+-- ...
141+--
142+-- In these cases, we can define a custom traversal strategy to override the
143+-- generic behaviour for certain types.
144+-- For a self-contained example, see the CustomChildren module in the tests directory.
145+
146+-- | The children of a type are the types of its fields.
147+-- The 'Children' type family maps a type @a@ to its set of children.
148+--
149+-- This type family is parameterized by a symbol @ch@ (that can be declared as
150+-- an empty data type).
151+-- The symbol 'ChGeneric' provides a default definition. You can create new
152+-- symbols to override the set of children of abstract, non-generic types.
153+--
154+-- The following example declares a @Custom@ symbol to redefine 'Children'
155+-- for some abstract types from the @time@ library.
156+--
157+-- @
158+-- data Custom
159+-- type instance 'Children' Custom a = ChildrenCustom a
160+--
161+-- type family ChildrenCustom (a :: Type) where
162+-- ChildrenCustom DiffTime = '[]
163+-- ChildrenCustom NominalDiffTime = '[]
164+-- -- Add more custom mappings here.
165+--
166+-- ChildrenCustom a = Children ChGeneric a
167+-- @
168+--
169+-- To use this definition, replace 'types' with @'typesUsing' \@Custom@.
170+type family Children (ch :: Type) (a :: Type) :: [Type]
171+
172+-- | @since 1.2.0.0
173+typesUsing :: forall ch a s. HasTypesUsing ch s a => Traversal' s a
174+typesUsing = typesUsing_ @ch @s @a
175+{-# INLINE typesUsing #-}
176+
177+-- | @since 1.2.0.0
178+class HasTypesUsing (ch :: Type) s a where
179+ typesUsing_ :: Traversal' s a
180+
181+instance {-# OVERLAPPABLE #-}
182+ ( HasTypesOpt ch (Interesting ch a s) s a
183+ ) => HasTypesUsing ch s a where
184+ typesUsing_ = typesOpt @ch @(Interesting ch a s)
185+ {-# INLINE typesUsing_ #-}
186+
187+-- TODO: should this instance be incoherent?
188+instance {-# OVERLAPPABLE #-} HasTypesUsing ch a a where
189+ typesUsing_ = id
190+
191+-- | By adding instances to this class, we can override the default
192+-- behaviour in an ad-hoc manner.
193+-- For example:
194+--
195+-- @
196+-- instance HasTypesCustom Custom Opaque String where
197+-- typesCustom f (Opaque str) = Opaque <$> f str
198+-- @
199+--
200+-- @since 1.2.0.0
201+class HasTypesCustom (ch :: Type) s a where
202+ -- | This function should never be used directly, only to override
203+ -- the default traversal behaviour. To actually use the custom
204+ -- traversal strategy, see 'typesUsing'. This is because 'typesUsing' does
205+ -- additional optimisations, like ensuring that nodes with no relevant members will
206+ -- not be traversed at runtime.
207+ typesCustom :: Traversal' s a
208+
209+instance {-# OVERLAPPABLE #-}
210+ ( GHasTypes ch (Rep s) a
211+ , Generic s
212+ -- if there's no Generic instance here, it means we got through the
213+ -- Children check by a user-defined custom strategy.
214+ -- Therefore, we can ignore the missing Generic instance, and
215+ -- instead report a missing HasTypesCustom instance
216+ , Defined (Rep s)
217+ (PrettyError '[ 'Text "No instance " ':<>: QuoteType (HasTypesCustom ch s a)])
218+ (() :: Constraint)
219+ ) => HasTypesCustom ch s a where
220+ typesCustom f s = to <$> gtypes_ @ch f (from s)
221+ --{-# INLINE types' #-}
222+
223+
224+-- | The default definition of 'Children'.
225+-- Primitive types from core libraries have no children, and other types are
226+-- assumed to be 'Generic'.
227+data ChGeneric
228+type instance Children ChGeneric a = ChildrenDefault a
229+
230+type family ChildrenDefault (a :: Type) :: [Type] where
231+ ChildrenDefault Char = '[]
232+ ChildrenDefault Double = '[]
233+ ChildrenDefault Float = '[]
234+ ChildrenDefault Integer = '[]
235+ ChildrenDefault Int = '[]
236+ ChildrenDefault Int8 = '[]
237+ ChildrenDefault Int16 = '[]
238+ ChildrenDefault Int32 = '[]
239+ ChildrenDefault Int64 = '[]
240+ ChildrenDefault Word = '[]
241+ ChildrenDefault Word8 = '[]
242+ ChildrenDefault Word16 = '[]
243+ ChildrenDefault Word32 = '[]
244+ ChildrenDefault Word64 = '[]
245+ ChildrenDefault T.Text = '[]
246+ ChildrenDefault a
247+ = Defined (Rep a)
248+ (NoGeneric a
249+ '[ 'Text "arising from a generic traversal."
250+ , 'Text "Either derive the instance, or define a custom traversal using " ':<>: QuoteType HasTypesCustom
251+ ])
252+ (ChildrenGeneric (Rep a) '[])
253+
254+type family ChildrenGeneric (f :: k -> Type) (cs :: [Type]) :: [Type] where
255+ ChildrenGeneric (M1 _ _ f) cs = ChildrenGeneric f cs
256+ ChildrenGeneric (l :*: r) cs = ChildrenGeneric l (ChildrenGeneric r cs)
257+ ChildrenGeneric (l :+: r) cs = ChildrenGeneric l (ChildrenGeneric r cs)
258+ ChildrenGeneric (Rec0 a) cs = a ': cs
259+ ChildrenGeneric _ cs = cs
260+
261+--------------------------------------------------------------------------------
262+-- Internals
263+--------------------------------------------------------------------------------
264+-- TODO: these should never leak out in error messages
265+
266+class HasTypesOpt (ch :: Type) (t :: Bool) s a where
267+ typesOpt :: Traversal' s a
268+
269+instance HasTypesCustom ch s a => HasTypesOpt ch 'True s a where
270+ typesOpt = typesCustom @ch
271+
272+instance HasTypesOpt ch 'False s a where
273+ typesOpt _ = pure
274+ --{-# INLINE typesOpt #-}
275+
276+--------------------------------------------------------------------------------
277+
278+class GHasTypes ch s a where
279+ gtypes_ :: Traversal' (s x) a
280+
281+instance
282+ ( GHasTypes ch l a
283+ , GHasTypes ch r a
284+ ) => GHasTypes ch (l :*: r) a where
285+ gtypes_ f (l :*: r) = (:*:) <$> gtypes_ @ch f l <*> gtypes_ @ch f r
286+ {-# INLINE gtypes_ #-}
287+
288+instance
289+ ( GHasTypes ch l a
290+ , GHasTypes ch r a
291+ ) => GHasTypes ch (l :+: r) a where
292+ gtypes_ f (L1 l) = L1 <$> gtypes_ @ch f l
293+ gtypes_ f (R1 r) = R1 <$> gtypes_ @ch f r
294+ {-# INLINE gtypes_ #-}
295+
296+instance (GHasTypes ch s a) => GHasTypes ch (M1 m meta s) a where
297+ gtypes_ f (M1 s) = M1 <$> gtypes_ @ch f s
298+ {-# INLINE gtypes_ #-}
299+
300+-- | In the recursive case, we invoke 'HasTypesUsing' again, using the
301+-- same strategy
302+instance HasTypesUsing ch b a => GHasTypes ch (Rec0 b) a where
303+ gtypes_ f (K1 x) = K1 <$> typesUsing_ @ch @b @a f x
304+ {-# INLINE gtypes_ #-}
305+
306+-- | The default instance for 'HasTypes' acts as a synonym for
307+-- 'HasTypesUsing ChGeneric', so in most cases this instance should
308+-- behave the same as the one above.
309+-- However, there might be overlapping instances defined for
310+-- 'HasTypes' directly, in which case we want to prefer those
311+-- instances (even though the custom instances should always be added to 'HasTypesCustom')
312+instance {-# OVERLAPPING #-} HasTypes b a => GHasTypes ChGeneric (Rec0 b) a where
313+ gtypes_ f (K1 x) = K1 <$> types_ @b @a f x
314+ {-# INLINE gtypes_ #-}
315+
316+instance GHasTypes ch U1 a where
317+ gtypes_ _ _ = pure U1
318+ {-# INLINE gtypes_ #-}
319+
320+instance GHasTypes ch V1 a where
321+ gtypes_ _ = pure
322+ {-# INLINE gtypes_ #-}
323+
324+type Interesting (ch :: Type) (a :: Type) (t :: Type)
325+ = Defined_list (Children ch t) (NoChildren ch t)
326+ (IsNothing (Interesting' ch a '[t] (Children ch t)))
327+
328+type family NoChildren (ch :: Type) (a :: Type) :: Constraint where
329+ NoChildren ch a = PrettyError
330+ '[ 'Text "No type family instance for " ':<>: QuoteType (Children ch a)
331+ , 'Text "arising from a traversal over " ':<>: QuoteType a
332+ , 'Text "with custom strategy " ':<>: QuoteType ch
333+ ]
334+
335+
336+type family Interesting' (ch :: Type) (a :: Type) (seen :: [Type]) (ts :: [Type]) :: Maybe [Type] where
337+ Interesting' ch _ seen '[] = 'Just seen
338+ Interesting' ch a seen (t ': ts) =
339+ InterestingOr ch a (InterestingUnless ch a seen t (Elem t seen)) ts
340+
341+-- Short circuit
342+-- Note: we only insert 't' to the seen list if it's not already there (which is precisely when `s` is 'False)
343+type family InterestingUnless
344+ (ch :: Type) (a :: Type) (seen :: [Type]) (t :: Type) (alreadySeen :: Bool) ::
345+ Maybe [Type] where
346+ InterestingUnless ch a seen a _ = 'Nothing
347+ InterestingUnless ch a seen t 'True = 'Just seen
348+ InterestingUnless ch a seen t 'False
349+ = Defined_list (Children ch t) (NoChildren ch t)
350+ (Interesting' ch a (t ': seen) (Children ch t))
351+
352+-- Short circuit
353+type family InterestingOr
354+ (ch :: Type) (a :: Type) (seen' :: Maybe [Type]) (ts :: [Type]) ::
355+ Maybe [Type] where
356+ InterestingOr ch a 'Nothing _ = 'Nothing
357+ InterestingOr ch a ('Just seen) ts = Interesting' ch a seen ts
358+
359+type family Elem a as where
360+ Elem a (a ': _) = 'True
361+ Elem a (_ ': as) = Elem a as
362+ Elem a '[] = 'False
363+
364+type family IsNothing a where
365+ IsNothing ('Just _) = 'False
366+ IsNothing 'Nothing = 'True
367+
src/Data/Generics/Sum.hs+5−6
@@ -1,7 +1,7 @@
11-----------------------------------------------------------------------------
22-- |
33-- Module : Data.Generics.Sum
4−-- Copyright : (C) 2017 Csongor Kiss
4+-- Copyright : (C) 2019 Csongor Kiss
55-- License : BSD3
66-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
77-- Stability : experimental
@@ -17,11 +17,10 @@
1717
1818module Data.Generics.Sum
1919 ( -- *Prisms
20− AsAny (..)
21−
22− , AsConstructor (..)
23− , AsSubtype (..)
24− , AsType (..)
20+ module Data.Generics.Sum.Any
21+ , module Data.Generics.Sum.Constructors
22+ , module Data.Generics.Sum.Subtype
23+ , module Data.Generics.Sum.Typed
2524 ) where
2625
2726import Data.Generics.Sum.Any
src/Data/Generics/Sum/Any.hs+40−34
@@ -11,7 +11,7 @@
1111-----------------------------------------------------------------------------
1212-- |
1313-- Module : Data.Generics.Sum.Any
14−-- Copyright : (C) 2017 Csongor Kiss
14+-- Copyright : (C) 2019 Csongor Kiss
1515-- License : BSD3
1616-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1717-- Stability : experimental
@@ -24,67 +24,73 @@
2424module Data.Generics.Sum.Any
2525 ( -- *Prisms
2626 --
27− -- $example
27+ -- $setup
2828 AsAny (..)
2929 ) where
3030
31−import Data.Generics.Internal.Lens
3231import Data.Generics.Sum.Constructors
3332import Data.Generics.Sum.Typed
33+import Data.Generics.Internal.VL.Prism
3434
35−-- $example
36−-- @
37−-- module Example where
35+-- $setup
36+-- == /Running example:/
3837--
39−-- import Data.Generics.Sum
40−-- import GHC.Generics
41−--
42−-- data Animal
43−-- = Dog Dog
44−-- | Cat Name Age
45−-- | Duck Age
46−-- deriving (Generic, Show)
47−--
48−-- data Dog = MkDog
49−-- { name :: Name
50−-- , age :: Age
51−-- }
52−-- deriving (Generic, Show)
53−--
54−-- type Name = String
55−-- type Age = Int
56−--
57−-- dog, cat, duck :: Animal
58−--
59−-- dog = Dog (MkDog "Shep" 3)
60−-- cat = Cat "Mog" 5
61−-- duck = Duck 2
62−-- @
38+-- >>> :set -XTypeApplications
39+-- >>> :set -XDataKinds
40+-- >>> :set -XDeriveGeneric
41+-- >>> import GHC.Generics
42+-- >>> :m +Data.Generics.Internal.VL.Prism
43+-- >>> :{
44+-- data Animal
45+-- = Dog Dog
46+-- | Cat Name Age
47+-- | Duck Age
48+-- deriving (Generic, Show)
49+-- data Dog
50+-- = MkDog
51+-- { name :: Name
52+-- , age :: Age
53+-- }
54+-- deriving (Generic, Show)
55+-- type Name = String
56+-- type Age = Int
57+-- dog, cat, duck :: Animal
58+-- dog = Dog (MkDog "Shep" 3)
59+-- cat = Cat "Mog" 5
60+-- duck = Duck 2
61+-- :}
6362
6463-- |Sums that have generic prisms.
65−class AsAny (sel :: k) a s | s sel k -> a where
64+class AsAny sel a s | s sel -> a where
6665 -- |A prism that projects a sum as identified by some selector. Currently
6766 -- supported selectors are constructor names and unique types. Compatible
6867 -- with the lens package's 'Control.Lens.Prism' type.
6968 --
7069 -- >>> dog ^? _As @"Dog"
7170 -- Just (MkDog {name = "Shep", age = 3})
71+ --
7272 -- >>> dog ^? _As @Dog
7373 -- Just (MkDog {name = "Shep", age = 3})
74+ --
7475 -- >>> dog ^? _As @"Cat"
7576 -- Nothing
77+ --
7678 -- >>> cat ^? _As @(Name, Age)
7779 -- Just ("Mog",5)
80+ --
7881 -- >>> cat ^? _As @"Cat"
7982 -- Just ("Mog",5)
83+ --
8084 -- >>> _As @"Cat" # ("Garfield", 6) :: Animal
81− -- Cat ("Garfield",6)
85+ -- Cat "Garfield" 6
86+ --
8287 -- >>> duck ^? _As @Age
8388 -- Just 2
84− _As :: Prism' s a
89+ _As :: Prism s s a a
8590
86−instance AsConstructor ctor a s => AsAny ctor a s where
91+instance AsConstructor ctor s s a a => AsAny ctor a s where
8792 _As = _Ctor @ctor
8893
8994instance AsType a s => AsAny a a s where
9095 _As = _Typed @a
96+
src/Data/Generics/Sum/Constructors.hs+145−39
@@ -1,4 +1,5 @@
11{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
23{-# LANGUAGE DataKinds #-}
34{-# LANGUAGE FlexibleInstances #-}
45{-# LANGUAGE FunctionalDependencies #-}
@@ -9,11 +10,12 @@
910{-# LANGUAGE TypeFamilies #-}
1011{-# LANGUAGE TypeOperators #-}
1112{-# LANGUAGE UndecidableInstances #-}
13+{-# LANGUAGE FlexibleContexts #-}
1214
1315-----------------------------------------------------------------------------
1416-- |
1517-- Module : Data.Generics.Sum.Constructors
16−-- Copyright : (C) 2017 Csongor Kiss
18+-- Copyright : (C) 2019 Csongor Kiss
1719-- License : BSD3
1820-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1921-- Stability : experimental
@@ -26,72 +28,176 @@
2628module Data.Generics.Sum.Constructors
2729 ( -- *Prisms
2830
29− -- $example
31+ -- $setup
3032 AsConstructor (..)
33+ , AsConstructor_ (..)
34+ , AsConstructor' (..)
35+ , AsConstructor0 (..)
3136 ) where
3237
3338import Data.Generics.Internal.Families
34−import Data.Generics.Internal.Lens
39+import Data.Generics.Internal.Void
3540import Data.Generics.Sum.Internal.Constructors
3641
3742import Data.Kind (Constraint, Type)
3843import GHC.Generics (Generic (Rep))
3944import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
45+import Data.Generics.Internal.VL.Prism
46+import Data.Generics.Internal.Profunctor.Iso
47+import Data.Generics.Internal.Profunctor.Prism (prismPRavel)
48+import Data.Generics.Internal.Errors
4049
41−-- $example
42−-- @
43−-- module Example where
50+-- $setup
51+-- == /Running example:/
4452--
45−-- import Data.Generics.Sum
46−-- import GHC.Generics
47−--
48−-- data Animal
49−-- = Dog Dog
50−-- | Cat Name Age
51−-- | Duck Age
52−-- deriving (Generic, Show)
53−--
54−-- data Dog = MkDog
55−-- { name :: Name
56−-- , age :: Age
57−-- }
58−-- deriving (Generic, Show)
59−--
60−-- type Name = String
61−-- type Age = Int
62−--
63−-- dog, cat, duck :: Animal
64−--
65−-- dog = Dog (MkDog "Shep" 3)
66−-- cat = Cat "Mog" 5
67−-- duck = Duck 2
68−-- @
53+-- >>> :set -XTypeApplications
54+-- >>> :set -XDataKinds
55+-- >>> :set -XDeriveGeneric
56+-- >>> :set -XFlexibleContexts
57+-- >>> :set -XTypeFamilies
58+-- >>> import GHC.Generics
59+-- >>> :m +Data.Generics.Internal.VL.Prism
60+-- >>> :m +Data.Generics.Product.Fields
61+-- >>> :m +Data.Function
62+-- >>> :{
63+-- data Animal a
64+-- = Dog (Dog a)
65+-- | Cat Name Age
66+-- | Duck Age
67+-- deriving (Generic, Show)
68+-- data Dog a
69+-- = MkDog
70+-- { name :: Name
71+-- , age :: Age
72+-- , fieldA :: a
73+-- }
74+-- deriving Show
75+-- type Name = String
76+-- type Age = Int
77+-- dog, cat, duck :: Animal Int
78+-- dog = Dog (MkDog "Shep" 3 30)
79+-- cat = Cat "Mog" 5
80+-- duck = Duck 2
81+-- :}
6982
7083-- |Sums that have a constructor with a given name.
71−class AsConstructor (ctor :: Symbol) a s | s ctor -> a where
84+class AsConstructor (ctor :: Symbol) s t a b | ctor s -> a, ctor t -> b where
7285 -- |A prism that projects a named constructor from a sum. Compatible with the
7386 -- lens package's 'Control.Lens.Prism' type.
7487 --
7588 -- >>> dog ^? _Ctor @"Dog"
76− -- Just (MkDog {name = "Shep", age = 3})
89+ -- Just (MkDog {name = "Shep", age = 3, fieldA = 30})
7790 --
7891 -- >>> dog ^? _Ctor @"Cat"
7992 -- Nothing
8093 --
8194 -- >>> cat ^? _Ctor @"Cat"
82− -- Just ("Mog", 5)
95+ -- Just ("Mog",5)
96+ --
97+ -- >>> _Ctor @"Cat" # ("Garfield", 6) :: Animal Int
98+ -- Cat "Garfield" 6
8399 --
84− -- >>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
85− -- Cat ("Garfield", 6)
86− _Ctor :: Prism' s a
100+ -- === /Type errors/
101+ --
102+ -- >>> cat ^? _Ctor @"Turtle"
103+ -- ...
104+ -- ...
105+ -- ... The type Animal Int does not contain a constructor named "Turtle"
106+ -- ...
107+ _Ctor :: Prism s t a b
108+
109+-- |Sums that have a constructor with a given name.
110+--
111+-- The difference between 'HasConstructor' and 'HasConstructor_' is similar to
112+-- the one between 'Data.Generics.Product.Fields.HasField' and
113+-- 'Data.Generics.Product.Fields.HasField_'.
114+-- See 'Data.Generics.Product.Fields.HasField_'.
115+class AsConstructor_ (ctor :: Symbol) s t a b where
116+ _Ctor_ :: Prism s t a b
117+
118+class AsConstructor' (ctor :: Symbol) s a | ctor s -> a where
119+ _Ctor' :: Prism s s a a
120+
121+-- |Sums that have a constructor with a given name.
122+--
123+-- This class gives the minimal constraints needed to define this prism.
124+-- For common uses, see 'HasConstructor'.
125+class AsConstructor0 (ctor :: Symbol) s t a b where
126+ _Ctor0 :: Prism s t a b
87127
88128instance
89129 ( Generic s
90130 , ErrorUnless ctor s (HasCtorP ctor (Rep s))
91− , GAsConstructor ctor (Rep s) a
92− ) => AsConstructor ctor a s where
131+ , GAsConstructor' ctor (Rep s) a
132+ , Defined (Rep s)
133+ (NoGeneric s '[ 'Text "arising from a generic prism focusing on the "
134+ ':<>: QuoteType ctor ':<>: 'Text " constructor of type " ':<>: QuoteType a
135+ , 'Text "in " ':<>: QuoteType s])
136+ (() :: Constraint)
137+ ) => AsConstructor' ctor s a where
138+ _Ctor' eta = prismRavel (prismPRavel (repIso . _GCtor @ctor)) eta
139+ {-# INLINE[2] _Ctor' #-}
140+
141+instance
142+ ( ErrorUnless ctor s (HasCtorP ctor (Rep s))
143+ , GAsConstructor' ctor (Rep s) a -- TODO: add a test similar to #62 for prisms
144+ , GAsConstructor' ctor (Rep (Indexed s)) a'
145+ , GAsConstructor ctor (Rep s) (Rep t) a b
146+ , t ~ Infer s a' b
147+ , GAsConstructor' ctor (Rep (Indexed t)) b'
148+ , s ~ Infer t b' a
149+ , AsConstructor0 ctor s t a b
150+ ) => AsConstructor ctor s t a b where
151+
152+ _Ctor = _Ctor0 @ctor
153+ {-# INLINE[2] _Ctor #-}
154+
155+-- | See Note [Uncluttering type signatures]
156+#if __GLASGOW_HASKELL__ < 804
157+-- >>> :t _Ctor
158+-- _Ctor
159+-- :: (Applicative f, Data.Profunctor.Choice.Choice p,
160+-- AsConstructor ctor s t a b) =>
161+-- p a (f b) -> p s (f t)
162+#else
163+-- >>> :t _Ctor
164+-- _Ctor
165+-- :: (AsConstructor ctor s t a b, Data.Profunctor.Choice.Choice p,
166+-- Applicative f) =>
167+-- p a (f b) -> p s (f t)
168+#endif
169+instance {-# OVERLAPPING #-} AsConstructor ctor (Void1 a) (Void1 b) a b where
170+ _Ctor = undefined
93171
94− _Ctor = repIso . _GCtor @ctor
172+instance
173+ ( ErrorUnless ctor s (HasCtorP ctor (Rep s))
174+ , GAsConstructor' ctor (Rep s) a -- TODO: add a test similar to #62 for prisms
175+ , GAsConstructor' ctor (Rep (Indexed s)) a'
176+ , GAsConstructor ctor (Rep s) (Rep t) a b
177+ , GAsConstructor' ctor (Rep (Indexed t)) b'
178+ , UnifyHead s t
179+ , UnifyHead t s
180+ , AsConstructor0 ctor s t a b
181+ ) => AsConstructor_ ctor s t a b where
182+
183+ _Ctor_ = _Ctor0 @ctor
184+ {-# INLINE[2] _Ctor_ #-}
185+
186+instance {-# OVERLAPPING #-} AsConstructor_ ctor (Void1 a) (Void1 b) a b where
187+ _Ctor_ = undefined
188+
189+instance
190+ ( Generic s
191+ , Generic t
192+ , GAsConstructor ctor (Rep s) (Rep t) a b
193+ , Defined (Rep s)
194+ (NoGeneric s '[ 'Text "arising from a generic prism focusing on the "
195+ ':<>: QuoteType ctor ':<>: 'Text " constructor of type " ':<>: QuoteType a
196+ , 'Text "in " ':<>: QuoteType s])
197+ (() :: Constraint)
198+ ) => AsConstructor0 ctor s t a b where
199+ _Ctor0 = prismRavel (prismPRavel (repIso . _GCtor @ctor))
200+ {-# INLINE[2] _Ctor0 #-}
95201
96202type family ErrorUnless (ctor :: Symbol) (s :: Type) (contains :: Bool) :: Constraint where
97203 ErrorUnless ctor s 'False
src/Data/Generics/Sum/Internal/Constructors.hs+29−16
@@ -1,4 +1,5 @@
11{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
23{-# LANGUAGE DataKinds #-}
34{-# LANGUAGE FlexibleInstances #-}
45{-# LANGUAGE FunctionalDependencies #-}
@@ -13,7 +14,7 @@
1314-----------------------------------------------------------------------------
1415-- |
1516-- Module : Data.Generics.Sum.Internal.Constructors
16−-- Copyright : (C) 2017 Csongor Kiss
17+-- Copyright : (C) 2019 Csongor Kiss
1718-- License : BSD3
1819-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1920-- Stability : experimental
@@ -25,39 +26,51 @@
2526
2627module Data.Generics.Sum.Internal.Constructors
2728 ( GAsConstructor (..)
29+ , GAsConstructor'
2830 ) where
2931
3032import Data.Generics.Internal.Families
31−import Data.Generics.Internal.HList
32−import Data.Generics.Internal.Lens
33+import Data.Generics.Product.Internal.HList
34+import Data.Profunctor (Profunctor(..))
3335
34−import Data.Kind (Type)
3536import GHC.Generics
3637import GHC.TypeLits (Symbol)
38+import Data.Generics.Internal.Profunctor.Lens
39+import Data.Generics.Internal.Profunctor.Iso
40+import Data.Generics.Internal.Profunctor.Prism
3741
3842-- |As 'AsConstructor' but over generic representations as defined by
3943-- "GHC.Generics".
40−class GAsConstructor (ctor :: Symbol) (f :: Type -> Type) a | ctor f -> a where
41− _GCtor :: Prism' (f x) a
44+class GAsConstructor (ctor :: Symbol) s t a b | ctor s -> a, ctor t -> b where
45+ _GCtor :: Prism (s x) (t x) a b
46+
47+type GAsConstructor' ctor s a = GAsConstructor ctor s s a a
4248
4349instance
44− ( GCollectible f as
50+ ( GIsList f f as as
51+ , GIsList g g bs bs
4552 , ListTuple a as
46− ) => GAsConstructor ctor (M1 C ('MetaCons ctor fixity fields) f) a where
53+ , ListTuple b bs
54+ ) => GAsConstructor ctor (M1 C ('MetaCons ctor fixity fields) f) (M1 C ('MetaCons ctor fixity fields) g) a b where
4755
48− _GCtor = prism (M1 . gfromCollection . tupleToList) (Right . listToTuple @_ @as . gtoCollection . unM1)
56+ _GCtor = dimap (listToTuple . view glist . unM1) (M1 . view (fromIso glist) . tupleToList)
57+ {-# INLINE[0] _GCtor #-}
4958
50−instance GSumAsConstructor ctor l r a (HasCtorP ctor l) => GAsConstructor ctor (l :+: r) a where
51− _GCtor = _GSumCtor @ctor @l @r @a @(HasCtorP ctor l)
59+instance GSumAsConstructor ctor (HasCtorP ctor l) l r l' r' a b => GAsConstructor ctor (l :+: r) (l' :+: r') a b where
60+ _GCtor = _GSumCtor @ctor @(HasCtorP ctor l)
61+ {-# INLINE[0] _GCtor #-}
5262
53−instance GAsConstructor ctor f a => GAsConstructor ctor (M1 D meta f) a where
63+instance GAsConstructor ctor f f' a b => GAsConstructor ctor (M1 D meta f) (M1 D meta f') a b where
5464 _GCtor = mIso . _GCtor @ctor
65+ {-# INLINE[0] _GCtor #-}
5566
56−class GSumAsConstructor (ctor :: Symbol) l r a (contains :: Bool) | ctor l r contains -> a where
57− _GSumCtor :: Prism' ((l :+: r) x) a
67+class GSumAsConstructor (ctor :: Symbol) (contains :: Bool) l r l' r' a b | ctor l r -> a, ctor l' r' -> b where
68+ _GSumCtor :: Prism ((l :+: r) x) ((l' :+: r') x) a b
5869
59−instance GAsConstructor ctor l a => GSumAsConstructor ctor l r a 'True where
70+instance GAsConstructor ctor l l' a b => GSumAsConstructor ctor 'True l r l' r a b where
6071 _GSumCtor = left . _GCtor @ctor
72+ {-# INLINE[0] _GSumCtor #-}
6173
62−instance GAsConstructor ctor r a => GSumAsConstructor ctor l r a 'False where
74+instance GAsConstructor ctor r r' a b => GSumAsConstructor ctor 'False l r l r' a b where
6375 _GSumCtor = right . _GCtor @ctor
76+ {-# INLINE[0] _GSumCtor #-}
src/Data/Generics/Sum/Internal/Subtype.hs+77−36
@@ -1,7 +1,10 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE DataKinds #-}
13{-# LANGUAGE FlexibleContexts #-}
24{-# LANGUAGE FlexibleInstances #-}
35{-# LANGUAGE KindSignatures #-}
46{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PolyKinds #-}
58{-# LANGUAGE ScopedTypeVariables #-}
69{-# LANGUAGE TypeApplications #-}
710{-# LANGUAGE TypeOperators #-}
@@ -11,7 +14,7 @@
1114-----------------------------------------------------------------------------
1215-- |
1316-- Module : Data.Generics.Sum.Internal.Subtype
14−-- Copyright : (C) 2017 Csongor Kiss
17+-- Copyright : (C) 2019 Csongor Kiss
1518-- License : BSD3
1619-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1720-- Stability : experimental
@@ -25,50 +28,88 @@ module Data.Generics.Sum.Internal.Subtype
2528 ( GAsSubtype (..)
2629 ) where
2730
28−import Data.Generics.Internal.HList
31+import Data.Generics.Product.Internal.HList
2932import Data.Generics.Sum.Internal.Typed
3033
3134import Data.Kind
3235import GHC.Generics
36+import Data.Generics.Internal.Profunctor.Iso
37+import Data.Generics.Internal.Profunctor.Prism
38+import Data.Generics.Internal.Families.Has
3339
3440-- |As 'AsSubtype' but over generic representations as defined by
3541-- "GHC.Generics".
3642class GAsSubtype (subf :: Type -> Type) (supf :: Type -> Type) where
37− ginjectSub :: subf x -> supf x
38− gprojectSub :: supf x -> Either (supf x) (subf x)
43+ _GSub :: Prism' (supf x) (subf x)
3944
4045instance
41− ( GAsSubtype l supf
42− , GAsSubtype r supf
43− ) => GAsSubtype (l :+: r) supf where
44−
45− ginjectSub x = case x of
46− L1 l -> ginjectSub l
47− R1 r -> ginjectSub r
48− gprojectSub x
49− = case gprojectSub x of
50− Left _ -> fmap R1 (gprojectSub x)
51− Right y -> Right (L1 y)
46+ ( GSplash sub sup
47+ , GDowncast sub sup
48+ ) => GAsSubtype sub sup where
49+ _GSub f = prism _GSplash _GDowncast f
50+ {-# INLINE[0] _GSub #-}
51+
52+--------------------------------------------------------------------------------
53+
54+class GSplash (sub :: Type -> Type) (sup :: Type -> Type) where
55+ _GSplash :: sub x -> sup x
56+
57+instance (GSplash a sup, GSplash b sup) => GSplash (a :+: b) sup where
58+ _GSplash (L1 rep) = _GSplash rep
59+ _GSplash (R1 rep) = _GSplash rep
60+ {-# INLINE[0] _GSplash #-}
61+
62+instance
63+ ( GIsList subf subf as as
64+ , GAsType supf as
65+ ) => GSplash (C1 meta subf) supf where
66+ _GSplash p = build (_GTyped . fromIso (mIso . glist)) p
67+ {-# INLINE[0] _GSplash #-}
68+
69+instance GSplash sub sup => GSplash (D1 c sub) sup where
70+ _GSplash (M1 m) = _GSplash m
71+ {-# INLINE[0] _GSplash #-}
72+
73+--------------------------------------------------------------------------------
74+
75+class GDowncast sub sup where
76+ _GDowncast :: sup x -> Either (sup x) (sub x)
5277
5378instance
54− ( GAsType supf a
55− , GCollectible subf as
56− , ListTuple a as
57− ) => GAsSubtype (C1 meta subf) supf where
58−
59− ginjectSub
60− = ginjectTyped . listToTuple . gtoCollection . unM1
61− gprojectSub
62− = fmap (M1 . gfromCollection . tupleToList) . gprojectTyped
63−
64−instance GAsType supf a => GAsSubtype (S1 meta (Rec0 a)) supf where
65− ginjectSub
66− = ginjectTyped @supf . unK1 . unM1
67− gprojectSub
68− = fmap (M1 . K1) . gprojectTyped @supf
69−
70−instance GAsSubtype subf supf => GAsSubtype (D1 meta subf) supf where
71− ginjectSub
72− = ginjectSub . unM1
73− gprojectSub
74− = fmap M1 . gprojectSub
79+ ( GIsList sup sup as as
80+ , GDowncastC (HasPartialTypeP as sub) sub sup
81+ ) => GDowncast sub (C1 m sup) where
82+ _GDowncast (M1 m) = case _GDowncastC @(HasPartialTypeP as sub) m of
83+ Left _ -> Left (M1 m)
84+ Right r -> Right r
85+ {-# INLINE[0] _GDowncast #-}
86+
87+instance (GDowncast sub l, GDowncast sub r) => GDowncast sub (l :+: r) where
88+ _GDowncast (L1 x) = case _GDowncast x of
89+ Left _ -> Left (L1 x)
90+ Right r -> Right r
91+ _GDowncast (R1 x) = case _GDowncast x of
92+ Left _ -> Left (R1 x)
93+ Right r -> Right r
94+ {-# INLINE[0] _GDowncast #-}
95+
96+instance GDowncast sub sup => GDowncast sub (D1 m sup) where
97+ _GDowncast (M1 m) = case _GDowncast m of
98+ Left _ -> Left (M1 m)
99+ Right r -> Right r
100+ {-# INLINE[0] _GDowncast #-}
101+
102+class GDowncastC (contains :: Bool) sub sup where
103+ _GDowncastC :: sup x -> Either (sup x) (sub x)
104+
105+instance GDowncastC 'False sub sup where
106+ _GDowncastC sup = Left sup
107+ {-# INLINE[0] _GDowncastC #-}
108+
109+instance
110+ ( GAsType sub subl
111+ , GIsList sup sup subl subl
112+ ) => GDowncastC 'True sub sup where
113+ _GDowncastC sup = Right (build (_GTyped . fromIso glist) sup)
114+ {-# INLINE[0] _GDowncastC #-}
115+
src/Data/Generics/Sum/Internal/Typed.hs+38−54
@@ -1,19 +1,19 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeApplications #-}
9+{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE TypeOperators #-}
11+{-# LANGUAGE UndecidableInstances #-}
1212
1313-----------------------------------------------------------------------------
1414-- |
1515-- Module : Data.Generics.Sum.Internal.Typed
16−-- Copyright : (C) 2017 Csongor Kiss
16+-- Copyright : (C) 2019 Csongor Kiss
1717-- License : BSD3
1818-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1919-- Stability : experimental
@@ -29,60 +29,44 @@ module Data.Generics.Sum.Internal.Typed
2929
3030import Data.Kind
3131import GHC.Generics
32+import Data.Tagged
3233
3334import Data.Generics.Internal.Families
34−import Data.Generics.Internal.HList
35−import Data.Generics.Internal.Lens
35+import Data.Generics.Product.Internal.HList
36+import Data.Generics.Internal.Profunctor.Iso
37+import Data.Generics.Internal.Profunctor.Prism
3638
3739-- |As 'AsType' but over generic representations as defined by "GHC.Generics".
38−class GAsType (f :: Type -> Type) a where
39− _GTyped :: Prism' (f x) a
40− _GTyped = prism ginjectTyped gprojectTyped
40+class GAsType (f :: Type -> Type) (as :: [Type]) where
41+ _GTyped :: Prism (f x) (f x) (HList as) (HList as)
42+-- We create this specialised version as we use it in the subtype prism
43+-- If we don't create it, the opportunity for specialisation is only
44+-- created after specialisation happens, I think a late specialisation pass
45+-- would pick up this case.
46+{-# SPECIALISE _GTyped :: (Tagged b b -> Tagged t t) #-}
4147
42− ginjectTyped :: a -> f x
43− gprojectTyped :: f x -> Either (f x) a
4448
4549instance
46− ( GCollectible f as
47− , ListTuple a as
48− ) => GAsType (M1 C meta f) a where
50+ ( GIsList f f as as
51+ ) => GAsType (M1 C meta f) as where
52+ _GTyped = mIso . glist
53+ {-# INLINE[0] _GTyped #-}
4954
50− ginjectTyped
51− = M1 . gfromCollection . tupleToList
52− gprojectTyped
53− = Right . listToTuple . gtoCollection . unM1
54−
55−instance GSumAsType (HasPartialTypeTupleP a l) l r a => GAsType (l :+: r) a where
56− ginjectTyped
57− = ginjectSumTyped @(HasPartialTypeTupleP a l) @l @r @a
58− gprojectTyped
59− = gprojectSumTyped @(HasPartialTypeTupleP a l) @l @r @a
55+instance GSumAsType (HasPartialTypeP a l) l r a => GAsType (l :+: r) a where
56+ _GTyped = _GSumTyped @(HasPartialTypeP a l)
57+ {-# INLINE[0] _GTyped #-}
6058
6159instance GAsType f a => GAsType (M1 D meta f) a where
62− ginjectTyped
63− = M1 . ginjectTyped
64− gprojectTyped
65− = either (Left . M1) Right . gprojectTyped . unM1
66−
67−class GSumAsType (contains :: Bool) l r a where
68− _GSumTyped :: Prism' ((l :+: r) x) a
69− _GSumTyped = prism (ginjectSumTyped @contains) (gprojectSumTyped @contains)
60+ _GTyped = mIso . _GTyped
61+ {-# INLINE[0] _GTyped #-}
7062
71− ginjectSumTyped :: a -> (l :+: r) x
72− gprojectSumTyped :: (l :+: r) x -> Either ((l :+: r) x) a
63+class GSumAsType (contains :: Bool) l r (a :: [Type]) where
64+ _GSumTyped :: Prism ((l :+: r) x) ((l :+: r) x) (HList a) (HList a)
7365
7466instance GAsType l a => GSumAsType 'True l r a where
75− ginjectSumTyped
76− = L1 . ginjectTyped
77− gprojectSumTyped x
78− = case x of
79− L1 l -> either (Left . L1) Right (gprojectTyped l)
80− R1 _ -> Left x
67+ _GSumTyped = left . _GTyped
68+ {-# INLINE[0] _GSumTyped #-}
8169
8270instance GAsType r a => GSumAsType 'False l r a where
83− ginjectSumTyped
84− = R1 . ginjectTyped
85− gprojectSumTyped x
86− = case x of
87− R1 r -> either (Left . R1) Right (gprojectTyped r)
88− L1 _ -> Left x
71+ _GSumTyped = right . _GTyped
72+ {-# INLINE[0] _GSumTyped #-}
src/Data/Generics/Sum/Subtype.hs+96−46
@@ -1,7 +1,10 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE FlexibleContexts #-}
23{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE MonoLocalBinds #-}
35{-# LANGUAGE MultiParamTypeClasses #-}
46{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE TypeApplications #-}
58{-# LANGUAGE TypeOperators #-}
69{-# LANGUAGE TypeSynonymInstances #-}
710{-# LANGUAGE UndecidableInstances #-}
@@ -9,7 +12,7 @@
912-----------------------------------------------------------------------------
1013-- |
1114-- Module : Data.Generics.Sum.Subtype
12−-- Copyright : (C) 2017 Csongor Kiss
15+-- Copyright : (C) 2019 Csongor Kiss
1316-- License : BSD3
1417-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1518-- Stability : experimental
@@ -22,53 +25,50 @@
2225module Data.Generics.Sum.Subtype
2326 ( -- *Prisms
2427 --
25− -- $example
28+ -- $setup
2629 AsSubtype (..)
2730 ) where
2831
29−import Data.Generics.Internal.Lens
32+import Data.Generics.Internal.Void
3033import Data.Generics.Sum.Internal.Subtype
3134
32−import GHC.Generics (Generic (Rep, to, from))
35+import GHC.Generics (Generic (Rep))
36+import Data.Generics.Internal.VL.Prism
37+import Data.Generics.Internal.Profunctor.Iso
3338
34−-- $example
35−-- @
36−-- module Example where
39+-- $setup
40+-- == /Running example:/
3741--
38−-- import Data.Generics.Sum
39−-- import GHC.Generics
40−--
41−-- data Animal
42−-- = Dog Dog
43−-- | Cat Name Age
44−-- | Duck Age
45−-- deriving (Generic, Show)
46−--
47−-- data FourLeggedAnimal
48−-- = Dog4 Dog
49−-- | Cat4 Name Age
50−-- deriving (Generic, Show)
51−--
52−-- data Dog = MkDog
53−-- { name :: Name
54−-- , age :: Age
55−-- }
56−-- deriving (Generic, Show)
57−--
58−-- type Name = String
59−-- type Age = Int
60−--
61−-- dog, cat, duck :: Animal
62−--
63−-- dog = Dog (MkDog "Shep" 3)
64−-- cat = Cat "Mog" 5
65−-- duck = Duck 2
66−--
67−-- dog4, cat4 :: FourLeggedAnimal
68−--
69−-- dog4 = Dog4 (MkDog "Snowy" 4)
70−-- cat4 = Cat4 "Garfield" 6
71−-- @
42+-- >>> :set -XTypeApplications
43+-- >>> :set -XDataKinds
44+-- >>> :set -XDeriveGeneric
45+-- >>> import GHC.Generics
46+-- >>> :m +Data.Generics.Internal.VL.Prism
47+-- >>> :{
48+-- data Animal
49+-- = Dog Dog
50+-- | Cat Name Age
51+-- | Duck Age
52+-- deriving (Generic, Show)
53+-- data FourLeggedAnimal
54+-- = Dog4 Dog
55+-- | Cat4 Name Age
56+-- deriving (Generic, Show)
57+-- data Dog = MkDog
58+-- { name :: Name
59+-- , age :: Age
60+-- }
61+-- deriving (Generic, Show)
62+-- type Name = String
63+-- type Age = Int
64+-- dog, cat, duck :: Animal
65+-- dog = Dog (MkDog "Shep" 3)
66+-- cat = Cat "Mog" 5
67+-- duck = Duck 2
68+-- dog4, cat4 :: FourLeggedAnimal
69+-- dog4 = Dog4 (MkDog "Snowy" 4)
70+-- cat4 = Cat4 "Garfield" 6
71+-- :}
7272
7373-- |Structural subtyping between sums. A sum 'Sub' is a subtype of another sum
7474-- 'Sup' if a value of 'Sub' can be given (modulo naming of constructors)
@@ -83,20 +83,27 @@ class AsSubtype sub sup where
8383 --
8484 -- >>> _Sub # dog4 :: Animal
8585 -- Dog (MkDog {name = "Snowy", age = 4})
86+ --
8687 -- >>> cat ^? _Sub :: Maybe FourLeggedAnimal
8788 -- Just (Cat4 "Mog" 5)
89+ --
8890 -- >>> duck ^? _Sub :: Maybe FourLeggedAnimal
8991 -- Nothing
9092 _Sub :: Prism' sup sub
91− _Sub = prism injectSub projectSub
93+ _Sub = prism injectSub (\i -> maybe (Left i) Right (projectSub i))
94+ {-# INLINE[2] _Sub #-}
9295
9396 -- |Injects a subtype into a supertype (upcast).
9497 injectSub :: sub -> sup
98+ injectSub
99+ = build (_Sub @sub @sup)
95100
96101 -- |Projects a subtype from a supertype (downcast).
97− projectSub :: sup -> Either sup sub
102+ projectSub :: sup -> Maybe sub
103+ projectSub
104+ = either (const Nothing) Just . match (_Sub @sub @sup)
98105
99− {-# MINIMAL injectSub, projectSub #-}
106+ {-# MINIMAL (injectSub, projectSub) | _Sub #-}
100107
101108instance
102109 ( Generic sub
@@ -104,5 +111,48 @@ instance
104111 , GAsSubtype (Rep sub) (Rep sup)
105112 ) => AsSubtype sub sup where
106113
107− injectSub = to . ginjectSub . from
108− projectSub = either (Left . to) (Right . to) . gprojectSub . from
114+ _Sub f = prismRavel (repIso . _GSub . fromIso repIso) f
115+ {-# INLINE[2] _Sub #-}
116+
117+-- | Reflexive case
118+-- >>> _Sub # dog :: Animal
119+-- Dog (MkDog {name = "Shep", age = 3})
120+instance {-# OVERLAPPING #-} AsSubtype a a where
121+ _Sub = id
122+ {-# INLINE[2] _Sub #-}
123+
124+-- | See Note [Uncluttering type signatures]
125+#if __GLASGOW_HASKELL__ < 804
126+-- >>> :t _Sub
127+--_Sub
128+-- :: (Applicative f, Data.Profunctor.Choice.Choice p,
129+-- AsSubtype sub sup) =>
130+-- p sub (f sub) -> p sup (f sup)
131+-- >>> :t _Sub
132+#else
133+--_Sub
134+-- :: (AsSubtype sub sup, Data.Profunctor.Choice.Choice p,
135+-- Applicative f) =>
136+-- p sub (f sub) -> p sup (f sup)
137+#endif
138+instance {-# OVERLAPPING #-} AsSubtype a Void where
139+ injectSub = undefined
140+ projectSub = undefined
141+
142+-- | See Note [Uncluttering type signatures]
143+#if __GLASGOW_HASKELL__ < 804
144+-- >>> :t _Sub @Int
145+-- _Sub @Int
146+-- :: (Applicative f, Data.Profunctor.Choice.Choice p,
147+-- AsSubtype Int sup) =>
148+-- p Int (f Int) -> p sup (f sup)
149+#else
150+-- >>> :t _Sub @Int
151+-- _Sub @Int
152+-- :: (AsSubtype Int sup, Data.Profunctor.Choice.Choice p,
153+-- Applicative f) =>
154+-- p Int (f Int) -> p sup (f sup)
155+#endif
156+instance {-# OVERLAPPING #-} AsSubtype Void a where
157+ injectSub = undefined
158+ projectSub = undefined
src/Data/Generics/Sum/Typed.hs+110−54
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeApplications #-}
13{-# LANGUAGE AllowAmbiguousTypes #-}
24{-# LANGUAGE DataKinds #-}
35{-# LANGUAGE FlexibleContexts #-}
@@ -12,7 +14,7 @@
1214-----------------------------------------------------------------------------
1315-- |
1416-- Module : Data.Generics.Sum.Typed
15−-- Copyright : (C) 2017 Csongor Kiss
17+-- Copyright : (C) 2019 Csongor Kiss
1618-- License : BSD3
1719-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
1820-- Stability : experimental
@@ -25,46 +27,50 @@
2527module Data.Generics.Sum.Typed
2628 ( -- *Prisms
2729 --
28− -- $example
30+ -- $setup
2931 AsType (..)
3032 ) where
3133
3234import Data.Kind
3335import GHC.Generics
34−import GHC.TypeLits (TypeError, ErrorMessage (..))
36+import GHC.TypeLits (TypeError, ErrorMessage (..), Symbol)
3537import Data.Generics.Sum.Internal.Typed
38+import Data.Generics.Internal.Errors
3639
3740import Data.Generics.Internal.Families
38−import Data.Generics.Internal.Lens
41+import Data.Generics.Internal.Void
42+import Data.Generics.Product.Internal.HList
43+import Data.Generics.Internal.VL.Prism
44+import Data.Generics.Internal.Profunctor.Iso
45+import Data.Generics.Internal.Profunctor.Prism (prismPRavel)
46+
47+-- $setup
48+-- >>> :set -XTypeApplications
49+-- >>> :set -XDataKinds
50+-- >>> :set -XDeriveGeneric
51+-- >>> import GHC.Generics
52+-- >>> :m +Data.Generics.Internal.VL.Prism
53+-- >>> :{
54+-- data Animal
55+-- = Dog Dog
56+-- | Cat Name Age
57+-- | Duck Age
58+-- | Turtle Age
59+-- deriving (Generic, Show)
60+-- data Dog
61+-- = MkDog
62+-- { name :: Name
63+-- , age :: Age
64+-- }
65+-- deriving (Generic, Show)
66+-- type Name = String
67+-- newtype Age = Age Int deriving Show
68+-- dog, cat, duck :: Animal
69+-- dog = Dog (MkDog "Shep" (Age 3))
70+-- cat = Cat "Mog" (Age 5)
71+-- duck = Duck (Age 2)
72+-- :}
3973
40−-- $example
41−-- @
42−-- module Example where
43−--
44−-- import Data.Generics.Sum
45−-- import GHC.Generics
46−--
47−-- data Animal
48−-- = Dog Dog
49−-- | Cat Name Age
50−-- | Duck Age
51−-- deriving (Generic, Show)
52−--
53−-- data Dog = MkDog
54−-- { name :: Name
55−-- , age :: Age
56−-- }
57−-- deriving (Generic, Show)
58−--
59−-- type Name = String
60−-- type Age = Int
61−--
62−-- dog, cat, duck :: Animal
63−--
64−-- dog = Dog (MkDog "Shep" 3)
65−-- cat = Cat "Mog" 5
66−-- duck = Duck 2
67−-- @
6874
6975-- |Sums that have a constructor with a field of the given type.
7076class AsType a s where
@@ -72,36 +78,88 @@ class AsType a s where
7278 -- its field. Compatible with the lens package's 'Control.Lens.Prism' type.
7379 --
7480 -- >>> dog ^? _Typed @Dog
75− -- Just (MkDog {name = "Shep", age = 3})
76− -- >>> dog ^? _Typed @Age
77− -- Nothing
81+ -- Just (MkDog {name = "Shep", age = Age 3})
7882 -- >>> cat ^? _Typed @(Name, Age)
79− -- Just ("Mog",5)
80− -- >>> duck ^? _Typed @Age
81− -- Just 2
83+ -- Just ("Mog",Age 5)
84+ -- >>> dog ^? _Typed @Age
85+ -- ...
86+ -- ...
87+ -- ... The type Animal contains multiple constructors whose fields are of type Age.
88+ -- ... The choice of constructor is thus ambiguous, could be any of:
89+ -- ... Duck
90+ -- ... Turtle
91+ -- ...
8292 _Typed :: Prism' s a
93+ _Typed = prism injectTyped (\i -> maybe (Left i) Right (projectTyped i))
94+ {-# INLINE[2] _Typed #-}
8395
8496 -- |Inject by type.
8597 injectTyped :: a -> s
98+ injectTyped
99+ = build _Typed
86100
87101 -- |Project by type.
88102 projectTyped :: s -> Maybe a
103+ projectTyped
104+ = either (const Nothing) Just . match _Typed
105+
106+ {-# MINIMAL (injectTyped, projectTyped) | _Typed #-}
89107
90108instance
91109 ( Generic s
92− , ErrorUnlessOne a s (CountPartialType a (Rep s))
93− , GAsType (Rep s) a
110+ , ErrorUnlessOne a s (CollectPartialType as (Rep s))
111+ , as ~ TupleToList a
112+ , ListTuple a as
113+ , GAsType (Rep s) as
114+ , Defined (Rep s)
115+ (NoGeneric s '[ 'Text "arising from a generic prism focusing on a constructor of type " ':<>: QuoteType a])
116+ (() :: Constraint)
94117 ) => AsType a s where
95118
96− _Typed
97− = repIso . _GTyped
98− injectTyped
99− = to . ginjectTyped
100− projectTyped
101− = either (const Nothing) Just . gprojectTyped . from
119+ _Typed eta = prismRavel (prismPRavel (repIso . _GTyped @_ @as . tupled)) eta
120+ {-# INLINE[2] _Typed #-}
121+
122+-- | See Note [Uncluttering type signatures]
123+#if __GLASGOW_HASKELL__ < 804
124+-- >>> :t _Typed
125+-- _Typed
126+-- :: (Applicative f, Data.Profunctor.Choice.Choice p, AsType a s) =>
127+-- p a (f a) -> p s (f s)
128+#else
129+-- >>> :t _Typed
130+-- _Typed
131+-- :: (AsType a s, Data.Profunctor.Choice.Choice p, Applicative f) =>
132+-- p a (f a) -> p s (f s)
133+#endif
134+instance {-# OVERLAPPING #-} AsType a Void where
135+ _Typed = undefined
136+ injectTyped = undefined
137+ projectTyped = undefined
138+
139+-- | See Note [Uncluttering type signatures]
140+#if __GLASGOW_HASKELL__ < 804
141+-- >>> :t _Typed @Int
142+-- _Typed @Int
143+-- :: (Applicative f, Data.Profunctor.Choice.Choice p,
144+-- AsType Int s) =>
145+-- p Int (f Int) -> p s (f s)
146+#else
147+-- >>> :t _Typed @Int
148+-- _Typed @Int
149+-- :: (AsType Int s, Data.Profunctor.Choice.Choice p,
150+-- Applicative f) =>
151+-- p Int (f Int) -> p s (f s)
152+#endif
153+instance {-# OVERLAPPING #-} AsType Void a where
154+ _Typed = undefined
155+ injectTyped = undefined
156+ projectTyped = undefined
102157
103−type family ErrorUnlessOne (a :: Type) (s :: Type) (count :: Count) :: Constraint where
104− ErrorUnlessOne a s 'None
158+type family ErrorUnlessOne (a :: Type) (s :: Type) (ctors :: [Symbol]) :: Constraint where
159+ ErrorUnlessOne _ _ '[_]
160+ = ()
161+
162+ ErrorUnlessOne a s '[]
105163 = TypeError
106164 ( 'Text "The type "
107165 ':<>: 'ShowType s
@@ -109,14 +167,12 @@ type family ErrorUnlessOne (a :: Type) (s :: Type) (count :: Count) :: Constrain
109167 ':<>: 'ShowType a
110168 )
111169
112− ErrorUnlessOne a s 'Multiple
170+ ErrorUnlessOne a s cs
113171 = TypeError
114172 ( 'Text "The type "
115173 ':<>: 'ShowType s
116174 ':<>: 'Text " contains multiple constructors whose fields are of type "
117− ':<>: 'ShowType a
118− ':<>: 'Text "; the choice of constructor is thus ambiguous"
175+ ':<>: 'ShowType a ':<>: 'Text "."
176+ ':$$: 'Text "The choice of constructor is thus ambiguous, could be any of:"
177+ ':$$: ShowSymbols cs
119178 )
120−
121− ErrorUnlessOne _ _ 'One
122− = ()
src/Data/Generics/Wrapped.hsnew file+104
@@ -0,0 +1,104 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE TypeApplications #-}
3+{-# language ConstraintKinds #-}
4+{-# language DataKinds #-}
5+{-# language FlexibleContexts #-}
6+{-# language FlexibleInstances #-}
7+{-# language FunctionalDependencies #-}
8+{-# language MultiParamTypeClasses #-}
9+{-# language ScopedTypeVariables #-}
10+{-# language TypeFamilies #-}
11+{-# language TypeOperators #-}
12+{-# language UndecidableInstances #-}
13+
14+-----------------------------------------------------------------------------
15+-- |
16+-- Module : Data.Generics.Wrapped
17+-- Copyright : (C) 2019 Csongor Kiss
18+-- License : BSD3
19+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
20+-- Stability : experimental
21+-- Portability : non-portable
22+--
23+-- Derive an isomorphism between a newtype and its wrapped type.
24+--
25+-----------------------------------------------------------------------------
26+
27+module Data.Generics.Wrapped
28+ ( Wrapped (..)
29+ , wrappedTo
30+ , wrappedFrom
31+ , _Unwrapped
32+ , _Wrapped
33+ )
34+where
35+
36+import Control.Applicative (Const(..))
37+import Data.Generics.Internal.Profunctor.Iso
38+
39+import qualified Data.Generics.Internal.VL.Iso as VL
40+import Data.Generics.Internal.Families.Changing ( UnifyHead )
41+
42+import Data.Kind (Constraint)
43+import GHC.Generics
44+import GHC.TypeLits
45+
46+type family ErrorUnlessOnlyOne a b :: Constraint where
47+ ErrorUnlessOnlyOne t (M1 i k a) = ErrorUnlessOnlyOne t a
48+ ErrorUnlessOnlyOne t (K1 i a) = ()
49+ ErrorUnlessOnlyOne t a =
50+ TypeError ('ShowType t ':<>: 'Text " is not a single-constructor, single-field datatype")
51+
52+-- | @since 1.1.0.0
53+_Unwrapped :: Wrapped s t a b => VL.Iso s t a b
54+_Unwrapped = wrappedIso
55+{-# inline _Unwrapped #-}
56+
57+-- | @since 1.1.0.0
58+_Wrapped :: Wrapped s t a b => VL.Iso b a t s
59+_Wrapped = VL.fromIso wrappedIso
60+{-# inline _Wrapped #-}
61+
62+-- TODO: move this into doctets
63+
64+-- newtype FlippedEither a b = FlippedEither (Either b a)
65+-- deriving Generic
66+
67+-- test :: (a -> c) -> FlippedEither a b -> FlippedEither c b
68+-- test f = over wrappedIso (fmap f)
69+
70+class GWrapped s t a b | s -> a, t -> b, s b -> t, t a -> s where
71+ gWrapped :: Iso (s x) (t x) a b
72+
73+instance GWrapped s t a b => GWrapped (M1 i k s) (M1 i k t) a b where
74+ gWrapped = mIso . gWrapped
75+
76+instance (a ~ c, b ~ d) => GWrapped (K1 i a) (K1 i b) c d where
77+ gWrapped = kIso
78+
79+-- | @since 1.1.0.0
80+class Wrapped s t a b | s -> a, t -> b where
81+ -- | @since 1.1.0.0
82+ wrappedIso :: VL.Iso s t a b
83+
84+-- | @since 1.1.0.0
85+wrappedTo :: forall s t a b. Wrapped s t a b => s -> a
86+wrappedTo a = view (wrappedIso @s @t @a @b) a
87+ where view l s = getConst (l Const s)
88+{-# INLINE wrappedTo #-}
89+
90+-- | @since 1.1.0.0
91+wrappedFrom :: forall s t a b. Wrapped s t a b => b -> t
92+wrappedFrom a = view (VL.fromIso (wrappedIso @s @t @a @b)) a
93+ where view l s = getConst (l Const s)
94+{-# INLINE wrappedFrom #-}
95+
96+instance
97+ ( Generic s
98+ , Generic t
99+ , GWrapped (Rep s) (Rep t) a b
100+ , UnifyHead s t
101+ , UnifyHead t s
102+ ) => Wrapped s t a b where
103+ wrappedIso = iso2isovl (repIso . gWrapped)
104+ {-# INLINE wrappedIso #-}
stack.yaml+1−4
@@ -1,11 +1,8 @@
1−resolver: nightly-2017-09-06
1+resolver: nightly-2018-08-09
22
33packages:
44- '.'
55
6−extra-deps:
7−- 'ghc-proofs-0.1.1'
8−
96flags: {}
107
118extra-package-dbs: []
test/Bifunctor.hsnew file+47
@@ -0,0 +1,47 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE DeriveAnyClass #-}
4+{-# LANGUAGE DeriveGeneric #-}
5+{-# LANGUAGE FlexibleContexts #-}
6+{-# LANGUAGE MonoLocalBinds #-}
7+{-# LANGUAGE StandaloneDeriving #-}
8+{-# LANGUAGE TypeApplications #-}
9+
10+module Main (main) where
11+
12+import Control.Lens hiding (Bifunctor(..))
13+import Control.Monad (void)
14+import Data.Generics.Product
15+import GHC.Generics
16+import Test.HUnit
17+
18+main :: IO ()
19+main = void $ runTestTT $
20+ bimap (* 2) show mytree ~=? mytreeBimapped
21+
22+data Tree a w = Leaf a
23+ | Fork (Tree a w) (Tree a w)
24+ | WithWeight (Tree a w) w
25+ deriving (Show, Eq, Generic)
26+
27+instance Bifunctor Tree where
28+ bimap = gbimap
29+
30+mytree :: Tree Int Int
31+mytree = Fork (WithWeight (Leaf 42) 1)
32+ (WithWeight (Fork (Leaf 88) (Leaf 37)) 2)
33+
34+mytreeBimapped :: Tree Int String
35+mytreeBimapped = Fork (WithWeight (Leaf 84) "1")
36+ (WithWeight (Fork (Leaf 176) (Leaf 74)) "2")
37+
38+--------------------------------------------------------------------------------
39+
40+class Bifunctor p where
41+ bimap :: (a -> c) -> (b -> d) -> p a b -> p c d
42+
43+gbimap ::
44+ ( HasParam 0 (p a b) (p a d) b d
45+ , HasParam 1 (p a d) (p c d) a c
46+ ) => (a -> c) -> (b -> d) -> p a b -> p c d
47+gbimap f g s = s & param @0 %~ g & param @1 %~ f
test/CustomChildren.hsnew file+63
@@ -0,0 +1,63 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE DeriveGeneric #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE KindSignatures #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE TypeApplications #-}
7+{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE UndecidableInstances #-}
9+
10+module CustomChildren
11+ ( customTypesTest
12+ ) where
13+
14+import GHC.Generics
15+import Data.Generics.Product
16+import Test.HUnit
17+import Data.Generics.Internal.VL.Lens
18+import Data.Generics.Labels ()
19+import Data.Kind
20+
21+-- Opaque has no Generic instance
22+data Opaque = Opaque String
23+ deriving (Show, Eq)
24+
25+-- Hide does have a Generic instance, but we want to hide its contents
26+-- from the traversal
27+data Hide = Hide String
28+ deriving (Show, Generic, Eq)
29+
30+-- We first define a symbol for the custom traversal
31+data Custom
32+
33+type instance Children Custom a = ChildrenCustom a
34+
35+type family ChildrenCustom (a :: Type) where
36+ ChildrenCustom Opaque = '[String] -- here we state explicitly that Opaque contains a String
37+ ChildrenCustom Hide = '[] -- and hide the contents of Hide
38+ ChildrenCustom a = Children ChGeneric a -- for the rest, we defer to the generic children
39+
40+-- We define the traversal of Opaque like so:
41+instance HasTypesCustom Custom Opaque String where
42+ typesCustom f (Opaque str) = Opaque <$> f str
43+
44+customTypesTest1 :: Test
45+customTypesTest1
46+ = TestCase (assertEqual "foo" (over (typesUsing @Custom @String) (++ "!") original) expected)
47+ where original = (Opaque "foo", Hide "bar")
48+ expected = (Opaque "foo!", Hide "bar") -- only Opaque's String gets modified
49+
50+customTypesTest2 :: Test
51+customTypesTest2
52+ = TestCase (assertEqual "foo" (over (typesUsing @Custom @String) (++ "!") original) expected)
53+ where original = Opaque "foo"
54+ expected = Opaque "foo!"
55+
56+customTypesTest3 :: Test
57+customTypesTest3
58+ = TestCase (assertEqual "foo" (over (typesUsing @Custom @String) (++ "!") original) expected)
59+ where original = Hide "foo"
60+ expected = Hide "foo"
61+
62+customTypesTest :: Test
63+customTypesTest = TestList [customTypesTest1, customTypesTest2, customTypesTest3]
test/Spec.hs+234−18
@@ -1,6 +1,8 @@
1−{-# OPTIONS_GHC -O -fplugin GHC.Proof.Plugin #-}
1+{-# OPTIONS_GHC -O -fplugin Test.Inspection.Plugin #-}
2+{-# OPTIONS_GHC -dsuppress-all #-}
23
34{-# LANGUAGE AllowAmbiguousTypes #-}
5+{-# LANGUAGE CPP #-}
46{-# LANGUAGE DataKinds #-}
57{-# LANGUAGE DeriveGeneric #-}
68{-# LANGUAGE DuplicateRecordFields #-}
@@ -8,15 +10,37 @@
810{-# LANGUAGE RankNTypes #-}
911{-# LANGUAGE ScopedTypeVariables #-}
1012{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE TemplateHaskell #-}
14+{-# LANGUAGE OverloadedLabels #-}
1115
1216module Main where
1317
1418import GHC.Generics
1519import Data.Generics.Product
16−import GHC.Proof
20+import Data.Generics.Sum
21+import Test.Inspection
22+import Test.HUnit
23+import Util
24+import System.Exit
25+import Data.Generics.Internal.VL.Lens
26+import Data.Generics.Internal.VL.Prism
27+import Data.Generics.Internal.VL.Traversal
28+import Control.Lens (_1, (+~))
29+import Data.Function ((&))
30+import Data.Generics.Labels ()
31+
32+-- This is sufficient at we only want to test that they typecheck
33+import Test24 ()
34+import Test25 ()
35+
36+import CustomChildren (customTypesTest)
1737
1838main :: IO ()
19−main = putStrLn "Hello world"
39+main = do
40+ res <- runTestTT tests
41+ case errors res + failures res of
42+ 0 -> exitSuccess
43+ _ -> exitFailure
2044
2145data Record = MkRecord
2246 { fieldA :: Int
@@ -27,34 +51,226 @@ data Record2 = MkRecord2
2751 { fieldA :: Int
2852 } deriving Generic
2953
30−type Lens' s a = forall f. Functor f => (a -> f a) -> s -> f s
54+data Record3 a = MkRecord3
55+ { fieldA :: a
56+ , fieldB :: Bool
57+ } deriving (Generic, Show)
58+
59+data Record4 a = MkRecord4
60+ { fieldA :: a
61+ , fieldB :: a
62+ } deriving (Generic1)
3163
32−fieldALensManual :: Lens' Record Int
33−fieldALensManual f (MkRecord a b) = (\a' -> MkRecord a' b) <$> f a
64+data Record5 = MkRecord5
65+ { fieldA :: Int
66+ , fieldB :: Int
67+ , fieldC :: String
68+ , fieldD :: Int
69+ , fieldE :: Char
70+ , fieldF :: Int
71+ } deriving Generic
72+
73+typeChangingManual :: Lens (Record3 a) (Record3 b) a b
74+typeChangingManual f (MkRecord3 a b) = (\a' -> MkRecord3 a' b) <$> f a
75+
76+typeChangingManualCompose :: Lens (Record3 (Record3 a)) (Record3 (Record3 b)) a b
77+typeChangingManualCompose = typeChangingManual . typeChangingManual
3478
3579newtype L s a = L (Lens' s a)
3680
81+intTraversalManual :: Traversal' Record5 Int
82+intTraversalManual f (MkRecord5 a b c d e f') =
83+ pure (\a1 a2 a3 a4 -> MkRecord5 a1 a2 c a3 e a4) <*> f a <*> f b <*> f d <*> f f'
84+
85+intTraversalDerived :: Traversal' Record5 Int
86+intTraversalDerived = types
87+
88+fieldALensManual :: Lens' Record Int
89+fieldALensManual f (MkRecord a b) = (\a' -> MkRecord a' b) <$> f a
90+
3791subtypeLensManual :: Lens' Record Record2
3892subtypeLensManual f record
3993 = fmap (\ds -> case record of
4094 MkRecord _ b -> MkRecord (case ds of {MkRecord2 g1 -> g1}) b
4195 ) (f (MkRecord2 (case record of {MkRecord a _ -> a})))
4296
97+data Sum1 = A Char | B Int | C () | D () deriving (Generic, Show)
98+data Sum2 = A2 Char | B2 Int deriving (Generic, Show)
99+
100+data Sum3 a b c
101+ = A3 a a
102+ | B3 String b a a b
103+ | C3 c a Int
104+ deriving Generic
105+
106+sum3Param0Derived :: Traversal (Sum3 a b xxx) (Sum3 a b yyy) xxx yyy
107+sum3Param0Derived = param @0
108+
109+sum3Param0Manual :: Traversal (Sum3 a b xxx) (Sum3 a b yyy) xxx yyy
110+sum3Param0Manual _ (A3 a1 a2) = pure (A3 a1 a2)
111+sum3Param0Manual _ (B3 s b1 a1 a2 b2) = pure (B3 s b1 a1 a2 b2)
112+sum3Param0Manual f (C3 c a i) = pure (\c' -> C3 c' a i) <*> f c
113+
114+sum3Param1Derived :: Traversal (Sum3 a xxx c) (Sum3 a yyy c) xxx yyy
115+sum3Param1Derived = param @1
116+
117+sum3Param1Manual :: Traversal (Sum3 a xxx c) (Sum3 a yyy c) xxx yyy
118+sum3Param1Manual _ (A3 a1 a2) = pure (A3 a1 a2)
119+sum3Param1Manual f (B3 s b1 a1 a2 b2) = pure (\b1' b2' -> B3 s b1' a1 a2 b2') <*> f b1 <*> f b2
120+sum3Param1Manual _ (C3 c a i) = pure (C3 c a i)
121+
122+sum3Param2Derived :: Traversal (Sum3 xxx b c) (Sum3 yyy b c) xxx yyy
123+sum3Param2Derived = param @2
124+
125+sum3Param2Manual :: Traversal (Sum3 xxx b c) (Sum3 yyy b c) xxx yyy
126+sum3Param2Manual f (A3 a1 a2) = pure (\a1' a2' -> A3 a1' a2') <*> f a1 <*> f a2
127+sum3Param2Manual f (B3 s b1 a1 a2 b2) = pure (\a1' a2' -> B3 s b1 a1' a2' b2) <*> f a1 <*> f a2
128+sum3Param2Manual f (C3 c a i) = pure (\a' -> C3 c a' i) <*> f a
129+
130+sum1PrismManual :: Prism Sum1 Sum1 Int Int
131+sum1PrismManual eta = prism g f eta
132+ where
133+ f s1 = case s1 of
134+ B i -> Right i
135+ s -> Left s
136+ g = B
137+
138+sum1PrismManualChar :: Prism Sum1 Sum1 Char Char
139+sum1PrismManualChar eta = prism g f eta
140+ where
141+ f s1 = case s1 of
142+ A i -> Right i
143+ B _ -> Left s1
144+ C _ -> Left s1
145+ D _ -> Left s1
146+ g = A
147+
148+sum2PrismManual :: Prism Sum2 Sum2 Int Int
149+sum2PrismManual eta = prism g f eta
150+ where
151+ f s1 = case s1 of
152+ B2 i -> Right i
153+ s -> Left s
154+ g = B2
155+
156+
157+sum2PrismManualChar :: Prism Sum2 Sum2 Char Char
158+sum2PrismManualChar eta = prism g f eta
159+ where
160+ f s1 = case s1 of
161+ A2 i -> Right i
162+ s -> Left s
163+ g = A2
164+
165+-- Note we don't have a catch-all case because of #14684
166+subtypePrismManual :: Prism Sum1 Sum1 Sum2 Sum2
167+subtypePrismManual eta = prism g f eta
168+ where
169+ f s1 = case s1 of
170+ A c -> Right (A2 c)
171+ B i -> Right (B2 i)
172+ C _ -> Left s1
173+ D _ -> Left s1
174+ g (A2 c) = A c
175+ g (B2 i) = B i
176+
177+
43178--------------------------------------------------------------------------------
44179-- * Tests
45−-- The ghc-proofs plugin checks that the following equalities hold, by checking
46−-- that the LHSs and the RHSs are CSEd. This also means that the runtime
47−-- characteristics of the derived lenses is the same as the manually written
48−-- ones above.
180+-- The inspection-testing plugin checks that the following equalities hold, by
181+-- checking that the LHSs and the RHSs are CSEd. This also means that the
182+-- runtime characteristics of the derived lenses is the same as the manually
183+-- written ones above.
184+
185+fieldALensName :: Lens' Record Int
186+fieldALensName = field @"fieldA"
187+
188+fieldALensName_ :: Lens' Record Int
189+fieldALensName_ = field_ @"fieldA"
190+
191+fieldALensType :: Lens' Record Int
192+fieldALensType = typed @Int
193+
194+fieldALensPos :: Lens' Record Int
195+fieldALensPos = position @1
196+
197+fieldALensPos_ :: Lens' Record Int
198+fieldALensPos_ = position_ @1
199+
200+subtypeLensGeneric :: Lens' Record Record2
201+subtypeLensGeneric = super
202+
203+typeChangingGeneric :: Lens (Record3 a) (Record3 b) a b
204+typeChangingGeneric = #fieldA
205+
206+typeChangingGenericPos :: Lens (Record3 a) (Record3 b) a b
207+typeChangingGenericPos = position @1
208+
209+typeChangingGenericCompose :: Lens (Record3 (Record3 a)) (Record3 (Record3 b)) a b
210+typeChangingGenericCompose = #fieldA . #fieldA
211+
212+typeChangingGenericCompose_ :: Lens (Record3 (Record3 a)) (Record3 (Record3 b)) a b
213+typeChangingGenericCompose_ = field_ @"fieldA" . field_ @"fieldA"
214+
215+sum1PrismB :: Prism Sum1 Sum1 Int Int
216+sum1PrismB = _Ctor @"B"
217+
218+subtypePrismGeneric :: Prism Sum1 Sum1 Sum2 Sum2
219+subtypePrismGeneric = _Sub
220+
221+sum1TypePrism :: Prism Sum1 Sum1 Int Int
222+sum1TypePrism = _Typed @Int
223+
224+sum1TypePrismChar :: Prism Sum1 Sum1 Char Char
225+sum1TypePrismChar = _Typed @Char
226+
227+sum2TypePrism :: Prism Sum2 Sum2 Int Int
228+sum2TypePrism = _Typed @Int
49229
50−fieldP :: Proof
51−fieldP = L fieldALensManual === L (field @"fieldA")
230+sum2TypePrismChar :: Prism Sum2 Sum2 Char Char
231+sum2TypePrismChar = _Typed @Char
52232
53−typedP :: Proof
54−typedP = L fieldALensManual === L (typed @Int)
233+data SumOfProducts =
234+ RecA { _foo :: Int, valA :: String }
235+ | RecB { _foo :: Int, valB :: Bool }
236+ | RecC { _foo :: Int }
237+ deriving (Show, Eq, Generic)
55238
56−posP :: Proof
57−posP = L fieldALensManual === L (position @1)
239+tests :: Test
240+tests = TestList $ map mkHUnitTest
241+ [ $(inspectTest $ 'fieldALensManual === 'fieldALensName)
242+ , $(inspectTest $ 'fieldALensManual === 'fieldALensName_)
243+ , $(inspectTest $ 'fieldALensManual === 'fieldALensType)
244+ , $(inspectTest $ 'fieldALensManual === 'fieldALensPos)
245+ , $(inspectTest $ 'fieldALensManual === 'fieldALensPos_)
246+ , $(inspectTest $ 'subtypeLensManual === 'subtypeLensGeneric)
247+ , $(inspectTest $ 'typeChangingManual === 'typeChangingGeneric)
248+ , $(inspectTest $ 'typeChangingManual === 'typeChangingGenericPos)
249+ , $(inspectTest $ 'typeChangingManualCompose === 'typeChangingGenericCompose)
250+ , $(inspectTest $ 'typeChangingManualCompose === 'typeChangingGenericCompose_)
251+ , $(inspectTest $ 'sum1PrismManual === 'sum1PrismB)
252+ , $(inspectTest $ 'subtypePrismManual === 'subtypePrismGeneric)
253+ , $(inspectTest $ 'sum2PrismManualChar === 'sum2TypePrismChar)
254+ , $(inspectTest $ 'sum2PrismManual === 'sum2TypePrism)
255+ , $(inspectTest $ 'sum1PrismManualChar === 'sum1TypePrismChar)
256+ , $(inspectTest $ 'sum2PrismManualChar === 'sum2TypePrismChar)
257+ , $(inspectTest $ 'sum1PrismManual === 'sum1TypePrism)
258+ --, $(inspectTest $ 'intTraversalManual === 'intTraversalDerived)
259+ --, $(inspectTest $ 'sum3Param0Manual === 'sum3Param0Derived)
260+ -- TODO [1.0.0.0]: these tests pass with the new implementation
261+-- , $(inspectTest $ 'sum3Param1Manual === 'sum3Param1Derived)
262+-- , $(inspectTest $ 'sum3Param2Manual === 'sum3Param2Derived)
263+ ] ++
264+ -- Tests for overloaded labels
265+ [ (valLabel ^. #_foo ) ~=? 3
266+ , (valLabel & #_foo +~ 10 ) ~=? RecB 13 True
267+#if __GLASGOW_HASKELL__ >= 802
268+ , (valLabel ^? #_RecB ) ~=? Just (3, True)
269+ , (valLabel ^? #_RecB . _1 ) ~=? Just 3
270+ , (valLabel ^? #_RecC ) ~=? Nothing
271+#endif
272+ , customTypesTest
273+ ]
274+ where valLabel = RecB 3 True
58275
59−subtypeP :: Proof
60−subtypeP = L subtypeLensManual === L super
276+-- TODO: add test for traversals over multiple types
test/Test24.hsnew file+47
@@ -0,0 +1,47 @@
1+{-# LANGUAGE DataKinds, DeriveGeneric, TypeApplications #-}
2+
3+module Test24 where
4+
5+-- Test case from #24, comments preserved
6+import Control.Lens
7+import Data.Generics.Product.Fields
8+import Data.Generics.Product.Positions
9+import GHC.Generics
10+
11+data Foo a b = Foo { x1 :: a, x2 :: b } deriving (Generic, Show)
12+
13+data Bar a b = Bar { x3 :: Foo a b, x4 :: Int } deriving (Generic, Show)
14+
15+tup :: ((Int, Char), Int)
16+tup = ((1, 'a'), 2)
17+tup2, tup3, tup4 :: ((Char, Char), Int)
18+tup2 = tup & _1 . _1 %~ toEnum -- Works.
19+tup3 = tup & x %~ toEnum -- Works also with type annotation.
20+ where x :: Lens ((Int, Char), Int) ((Char, Char), Int) Int Char
21+ x = _1 . _1
22+-- Works.
23+tup4 = tup & position @1 . position @1 %~ toEnum
24+
25+foo :: Foo Int Char
26+foo = Foo 1 'a'
27+foo2, foo3 :: Foo Char Char
28+foo2 = foo & field @"x1" %~ toEnum -- Works when there's just one 'field'.
29+foo3 = foo & position @1 %~ toEnum -- Works when there's just one 'position'.
30+
31+bar :: Bar Int Char
32+bar = Bar (Foo 1 'a') 2
33+bar2, bar3, bar4 :: Bar Char Char
34+-- Doesn't work, error at first 'field' (Couldn't match type ‘Int’ with ‘Char’ arising from a use of ‘field’).
35+bar2 = bar & field @"x3" . field @"x1" %~ toEnum
36+-- Type annotation doesn't help.
37+bar3 = bar & l %~ toEnum
38+ where l :: Lens (Bar Int Char) (Bar Char Char) Int Char
39+ l = field @"x3" . field @"x1"
40+-- Doesn't work, error at first 'position' (Couldn't match type ‘Int’ with ‘Char’ arising from a use of ‘position’).
41+bar4 = bar & position @1 . position @1 %~ toEnum
42+-- Works if we stick to simple Lens' (modify to the same type).
43+bar5 :: Bar Int Char
44+bar5 = bar & field @"x3" . field @"x1" %~ (+1)
45+
46+main :: IO ()
47+main = print bar5
test/Test25.hsnew file+33
@@ -0,0 +1,33 @@
1+{-# LANGUAGE DuplicateRecordFields #-}
2+{-# LANGUAGE TypeApplications #-}
3+{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE DeriveGeneric #-}
5+{-# OPTIONS_GHC -Wall #-}
6+
7+module Test25 where
8+
9+import Control.Lens
10+import Data.Generics.Product
11+import GHC.Generics
12+
13+data Record1 = Record1
14+ { field1 :: Int
15+ , field2 :: Double
16+ } deriving (Generic)
17+
18+class Default a where
19+ def :: a
20+
21+instance Default Record1 where
22+ def = Record1 0 0.0
23+
24+f :: Record1 -> Int
25+f r = r ^. field @"field1"
26+
27+main :: IO ()
28+main = do
29+ print $ f def
30+ print $ f ( field @"field1" .~ 1 $ (def :: Record1))
31+ print $ f ( field @"field1" .~ 2 $ Record1 0 0.0)
32+ print $ f ( field @"field1" .~ (1 :: Int) $ def)
33+ print $ f ( position @1 .~ (1 :: Int) $ def)
test/Test40.hsnew file+22
@@ -0,0 +1,22 @@
1+{-# LANGUAGE TypeFamilies #-}
2+{-# LANGUAGE TypeApplications #-}
3+{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE DeriveGeneric #-}
5+{-# OPTIONS_GHC -Wall #-}
6+
7+module Test40 where
8+
9+import Data.Generics.Product
10+import GHC.Generics
11+
12+class MyClass a where
13+ data AssocData a
14+
15+instance MyClass Int where
16+ data AssocData Int = SomeData
17+ { val :: Int
18+ } deriving (Generic)
19+
20+main :: IO ()
21+main
22+ = print $ getField @"val" (SomeData 3)
test/Test62.hsnew file+20
@@ -0,0 +1,20 @@
1+{-# LANGUAGE DataKinds, DeriveGeneric, TypeApplications #-}
2+module Test62 (example, example_) where
3+import Data.Generics.Product (field, field_, position, position_)
4+import Data.Generics.Internal.VL.Lens (set)
5+import GHC.Generics (Generic)
6+
7+data Foo a = Foo { bar :: Bar a } deriving Generic
8+data Bar a = Bar { x :: a, y :: a } deriving Generic
9+
10+example :: Foo ()
11+example =
12+ set (field @"bar" . position @1) ()
13+ . set (position @1 . field @"y") ()
14+ $ Foo{ bar = Bar{ x = (), y = () } }
15+
16+example_ :: Foo ()
17+example_ =
18+ set (field_ @"bar" . position_ @1) ()
19+ . set (position_ @1 . field_ @"y") ()
20+ $ Foo{ bar = Bar{ x = (), y = () } }
test/Test63.hsnew file+12
@@ -0,0 +1,12 @@
1+{-# LANGUAGE DataKinds, DeriveGeneric, TypeApplications #-}
2+module Test63 (example) where
3+import Data.Generics.Product (types)
4+import Data.Generics.Internal.VL.Lens (over)
5+import Data.Word (Word32)
6+import GHC.Generics (Generic)
7+
8+data Record = Record {field1 :: Word32, field2 :: Int}
9+ deriving (Generic, Show)
10+
11+example :: Record
12+example = over (types @Int) (+1) (Record 0 0)
test/Test71.hsnew file+17
@@ -0,0 +1,17 @@
1+{-# LANGUAGE FlexibleContexts, DeriveGeneric, TypeApplications #-}
2+
3+module Test71 where
4+
5+import GHC.Generics
6+import Control.Lens
7+import Data.Generics.Product
8+
9+data Foobar = Foobar Int Char String
10+ deriving Generic
11+
12+foo :: HasType Foobar ctx => ctx -> Char
13+foo ctx = case view (typed @Foobar) ctx of
14+ Foobar _ c _ -> c
15+
16+bar :: Char
17+bar = foo (Foobar 3 'a' "Hello")
test/Util.hsnew file+11
@@ -0,0 +1,11 @@
1+module Util where
2+
3+import Test.Inspection
4+import Test.HUnit.Base
5+
6+mkHUnitTest :: Result -> Test
7+mkHUnitTest r = TestCase $
8+ case r of
9+ Success _s -> return ()
10+ Failure s -> assertFailure s
11+
test/doctest.hsnew file+8
@@ -0,0 +1,8 @@
1+import Test.DocTest
2+main
3+ = doctest
4+ [ "-isrc"
5+ , "src/Data/Generics/Product.hs"
6+ , "src/Data/Generics/Sum.hs"
7+ , "src/Data/Generics/Labels.hs"
8+ ]
test/syb/Tree.hsnew file+76
@@ -0,0 +1,76 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE DeriveGeneric #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE TypeApplications #-}
5+
6+{-
7+Example adapted from SYB
8+========================
9+-}
10+
11+module Main (main) where
12+
13+import Control.Lens
14+import Control.Monad (void)
15+import Data.Generics.Product
16+import GHC.Generics
17+import Test.HUnit
18+
19+main :: IO ()
20+main = void $ runTestTT tests
21+
22+-- A parameterised datatype for binary trees with data at the leafs
23+data Tree a w = Leaf a
24+ | Fork (Tree a w) (Tree a w)
25+ | WithWeight (Tree a w) w
26+ deriving (Show, Generic, Eq)
27+
28+-- A typical tree
29+mytree :: Tree Int Int
30+mytree = Fork (WithWeight (Leaf 42) 1)
31+ (WithWeight (Fork (Leaf 88) (Leaf 37)) 2)
32+
33+mytreeShown :: Tree String Int
34+mytreeShown = Fork (WithWeight (Leaf "42") 1)
35+ (WithWeight (Fork (Leaf "88") (Leaf "37")) 2)
36+
37+-- A polymorphic-recursive structure
38+data Poly a b
39+ = PNil
40+ | PCons a (Poly b a)
41+ deriving (Show, Generic)
42+
43+poly :: Poly Int String
44+poly = PCons 10 (PCons "hello" (PCons 20 (PCons "world" PNil)))
45+
46+-- Print everything like an Int in mytree
47+-- In fact, we show two attempts:
48+-- 1. print really just everything like an Int
49+-- 2. print everything wrapped with Leaf
50+-- So (1.) confuses leafs and weights whereas (2.) does not.
51+tests :: Test
52+tests = TestList
53+ [ toListOf (types @Int) mytree ~=? [42,1,88,37,2]
54+ , toListOf (param @1) mytree ~=? [42,88,37]
55+
56+ -- Things not (easily) doable in SYB:
57+ -- change type of Tree by mapping a function over the second (from the right) param
58+ , (mytree & param @1 %~ show) ~=? mytreeShown
59+ -- collect values in poly corresponding to the first param
60+ , toListOf (param @0) poly ~=? ["hello", "world"]
61+ -- collect all Ints inside poly
62+ , toListOf (types @Int) poly ~=? [10, 20]
63+ -- map length over the Strings, then collect all Ints
64+ , toListOf (types @Int) (poly & param @0 %~ length) ~=? [10, 5, 20, 5]
65+ -- map length over the Strings, then collect all the resulting Ints
66+ , toListOf (param @0) (poly & param @0 %~ length) ~=? [5,5]
67+ ]
68+
69+-- original code from SYB:
70+--tests = show ( listify (\(_::Int) -> True) mytree
71+-- , everything (++) ([] `mkQ` fromLeaf) mytree
72+-- ) ~=? output
73+-- where
74+-- fromLeaf :: Tree Int Int -> [Int]
75+-- fromLeaf (Leaf x) = [x]
76+-- fromLeaf _ = []