on master help sign in
.github/FUNDING.ymlnew file+12
since splitsnapshots
unifiedsplit
@@ -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
6.ghc*
6.ghc*
7*.hi
7*.hi
8*.o
8*.o
9+core-html
10+*~
11+*.org
12+cabal.project.local
.travis.yml+132−65
@@ -1,88 +1,155 @@
1# This Travis job script has been generated by a script via
1# This Travis job script has been generated by a script via
2#
2#
3−# make_travis_yml_2.hs 'generic-lens.cabal'
3+# haskell-ci '--config=cabal.haskell-ci' 'cabal.project'
4#
4#
5−# For more information, see https://github.com/hvr/multi-ghc-travis
5+# For more information, see https://github.com/haskell-CI/haskell-ci
6+#
7+# version: 0.5.20190908
6#
8#
7language: c
9language: c
8−sudo: false
10+dist: xenial
9−
10git:
11git:
11− submodules: false # whether to recursively clone submodules
12+ # whether to recursively clone submodules
12−
13+ submodules: false
13cache:
14cache:
14 directories:
15 directories:
15 - $HOME/.cabal/packages
16 - $HOME/.cabal/packages
16 - $HOME/.cabal/store
17 - $HOME/.cabal/store
17−
18before_cache:
18before_cache:
19− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
19+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
20 # remove files that are regenerated by 'cabal update'
20 # remove files that are regenerated by 'cabal update'
21− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
21+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
22− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
22+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
23− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
23+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
24− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
24+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
25− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
25+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
26−
26+ - rm -rfv $CABALHOME/packages/head.hackage
27matrix:
27matrix:
28 include:
28 include:
29− - compiler: "ghc-8.0.2"
29+ - compiler: ghc-8.8.1
30− # env: TEST=--disable-tests BENCH=--disable-benchmarks
30+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}}
31+ - compiler: ghc-8.6.5
31− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}
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"]}}
32− - compiler: "ghc-8.2.1"
35+ - compiler: ghc-8.4.1
33− # env: TEST=--disable-tests BENCH=--disable-benchmarks
36+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.1","cabal-install-3.0"]}}
37+ - compiler: ghc-8.2.2
34− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.1], sources: [hvr-ghc]}}
38+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-3.0"]}}
35−
39+ - compiler: ghc-8.0.2
40+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-3.0"]}}
36before_install:
41before_install:
37− - HC=${CC}
42+ - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
43+ - WITHCOMPILER="-w $HC"
44+ - HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//')
38− - HCPKG=${HC/ghc/ghc-pkg}
45+ - HCPKG="$HC-pkg"
39 - unset CC
46 - unset CC
40− - PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$PATH
47+ - CABAL=/opt/ghc/bin/cabal
41−
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
42install:
73install:
43− - cabal --version
74+ - ${CABAL} --version
44 - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
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
45− - BENCH=${BENCH---enable-benchmarks}
78+ - BENCH=--enable-benchmarks
46− - TEST=${TEST---enable-tests}
79+ - if [ $HCNUMVER -ge 80800 ] ; then BENCH=--disable-benchmarks ; fi
47− - HADDOCK=${HADDOCK-true}
80+ - HEADHACKAGE=false
48− - INSTALLED=${INSTALLED-true}
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
49− - travis_retry cabal update -v
103+ - travis_retry ${CABAL} v2-update -v
50− - sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
104+ # Generate cabal.project
105+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
106+ - touch cabal.project
107+ - |
51− - rm -fv cabal.project.local
108+ echo "packages: ." >> cabal.project
52− - "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
53− - if [ -f "./configure.ac" ]; then
113+ - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
54− (cd "."; autoreconf -i);
114+ - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
55− fi
115+ - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
56− - rm -f cabal.project.freeze
116+ - rm cabal.project.freeze
57− - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
117+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all | color_cabal_output
58− - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all
118+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all | color_cabal_output
59− - rm -rf "."/.ghc.environment.* "."/dist
60− - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
61−
62−# Here starts the actual work to be performed for the package under test;
63−# any command which exits with a non-zero exit code causes the build to fail.
64script:
119script:
65− # test that source-distributions can be generated
120+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
66− - (cd "."; cabal sdist)
121+ # Packaging...
122+ - ${CABAL} v2-sdist all | color_cabal_output
123+ # Unpacking...
67− - mv "."/dist/generic-lens-*.tar.gz ${DISTDIR}/
124+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
68− - cd ${DISTDIR}
125+ - cd ${DISTDIR} || false
69− - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
126+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
70− - "printf 'packages: generic-lens-*/*.cabal\n' > cabal.project"
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...
71 # this builds all libraries and executables (without tests/benchmarks)
139 # this builds all libraries and executables (without tests/benchmarks)
72− - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
140+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
73−
141+ # Building with tests and benchmarks...
74− # Build with installed constraints for packages in global-db
75− - if $INSTALLED; then
76− echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh;
77− else echo "Not building with installed constraints"; fi
78−
79 # build & run tests, build benchmarks
142 # build & run tests, build benchmarks
80− - cabal new-build -w ${HC} ${TEST} ${BENCH} all
143+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
81− - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} all; fi
144+ # Testing...
82−
145+ - if [ $HCNUMVER -lt 80600 ] ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
83− # haddock
146+ # cabal check...
84− - rm -rf ./dist-newstyle
147+ - (cd ${PKGDIR_generic_lens} && ${CABAL} -vnormal check)
85− - if $HADDOCK; then cabal new-haddock -w ${HC} --disable-tests --disable-benchmarks all; else echo "Skipping haddock generation";fi
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
86
153
87−# REGENDATA ["generic-lens.cabal"]
154+# REGENDATA ["--config=cabal.haskell-ci","cabal.project"]
88# EOF
155# EOF
ChangeLog.md+38−1
@@ -1,5 +1,42 @@
1+## 1.2.0.1
2+- Give HasAny/AsAny the same VTA behavior on 8.6 and 8.8 (Ryan Scott)
3+
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
8+
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+
1## 0.5.1.0
36## 0.5.1.0
2−- Add `IsLabel` orhpan instance in `Data.Generics.Labels`
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)
3- Fix error message in subtype lens
40- Fix error message in subtype lens
4
41
5## 0.5.0.0
42## 0.5.0.0
DList.hsdeleted−119
@@ -1,119 +0,0 @@
1−{-# LANGUAGE RankNTypes #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−module DList where
4−
5−type L f = (Unit f, Concat f, Nil f)
6−
7−class Concat f where
8− conc :: f a -> f a -> f a
9−
10−class Unit f where
11− unit :: a -> f a
12−
13−class Nil f where
14− nil :: f a
15−
16−instance Concat [] where
17− conc = (++)
18−
19−instance Unit [] where
20− unit a = [a]
21−
22−instance Nil [] where
23− nil = []
24−
25−
26−newtype StringWrapper a = StringWrapper String deriving Show
27−
28−instance Concat StringWrapper where
29− conc (StringWrapper s1) (StringWrapper s2) =
30− StringWrapper ("( " ++ s1 ++ " ++ " ++ s2 ++ " )")
31−
32−instance Unit StringWrapper where
33− unit a = StringWrapper "[val]"
34−
35−instance Nil StringWrapper where
36− nil = StringWrapper "[]"
37−
38−cons x fa = unit x `conc` fa
39−
40−u :: L f => a -> f a
41−u = unit
42−
43−
44−l1 :: List Int
45−l1 = ((u 1) `conc` (u 2)) `conc` ((u 3) `conc` (u 4))
46−
47−l2 :: List Int
48−l2 = ((u 1) `conc` (u 2)) `conc` (u 3)
49−
50−-- Observe the normalisation using l1' @StringWrapper
51−l1' :: forall f . L f => f Int
52−l1' = ravel l1
53−
54−l2' :: forall f . L f => f Int
55−l2' = ravel l2
56−
57−
58−-- Goal: Define a data type Norm such that Norm f a ~ f a but Norm
59−-- right associates the cons
60−
61−newtype MapK f a = MapK (f a -> f a)
62−
63−liftMapK :: Concat f => f a -> MapK f a
64−--liftMapK fa = (MapK (`conc` fa)) -- Right Associative
65−liftMapK fa = (MapK (fa `conc`)) -- Left Associative
66−
67−lowerMapK :: Nil f => MapK f a -> f a
68−lowerMapK (MapK fa) = fa nil
69−
70−(<<++>) :: f a -> MapK f a -> f a
71−(<<++>) fa (MapK k) = k fa
72−
73−instance Concat (MapK f) where
74−-- conc fa fb = MapK (\g -> g <<++> fa <<++> fb)
75− conc (MapK fa) (MapK fb) = MapK (fa . fb)
76−-- unit a = MapK (\fa -> unit a
77−
78−instance L f => (Unit (MapK f)) where
79− unit a = liftMapK (unit a)
80−
81−instance L f => (Nil (MapK f)) where
82− nil = liftMapK nil
83−
84−type List a = forall f . L f => f a
85−
86−ravel :: List a -> List a
87−ravel fa = lower fa
88−
89−-- First reassociate as this introduces a nil
90−-- then remove the nil to demonstrate the second pass
91−-- This is cool!
92−lower :: L f => MapK (PureK f) a -> f a
93−lower = lowerPureK . lowerMapK
94−
95−-- xs ++ [] == xs
96−
97−data PureK f a = Pure | Dirty (f a)
98−
99−liftPureK :: f a -> PureK f a
100−liftPureK = Dirty
101−
102−lowerPureK :: L f => PureK f a -> f a
103−lowerPureK Pure = nil
104−lowerPureK (Dirty fa) = fa
105−
106−instance Nil (PureK f) where
107− nil = Pure
108−
109−instance Concat f => (Concat (PureK f)) where
110− conc Pure fa = fa
111− conc fa Pure = fa
112− conc (Dirty fa) (Dirty fb) = Dirty (fa `conc` fb)
113−
114−instance Unit f => (Unit (PureK f)) where
115− unit a = Dirty (unit a)
116−
117−
118−
119−
LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c) 2017, Csongor Kiss
1+Copyright (c) 2019, Csongor Kiss
2
2
3All rights reserved.
3All rights reserved.
4
4
README.md+238−78
@@ -3,26 +3,69 @@
3[![Build 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)
4[![Hackage](https://img.shields.io/hackage/v/generic-lens.svg)](https://hackage.haskell.org/package/generic-lens)
4[![Hackage](https://img.shields.io/hackage/v/generic-lens.svg)](https://hackage.haskell.org/package/generic-lens)
5
5
6−Generically derive lenses and prisms for data types.
6+Generically derive traversals, lenses and prisms.
7
7
8Available on [Hackage](https://hackage.haskell.org/package/generic-lens)
8Available on [Hackage](https://hackage.haskell.org/package/generic-lens)
9
9
10−This package uses the GHC 8 `Generic` representation to derive various operations
10+This library uses `GHC.Generics` to derive efficient optics (traversals, lenses
11−on data structures with lens interfaces, including structural subtype
11+and prisms) for algebraic data types in a type-directed way, with a focus on
12−relationships between records and positional indexing into arbitrary product
12+good type inference and error messages when possible.
13−types.
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 #-}
14
55
15−This is made possible by GHC 8's new Generics API, which provides metadata
56+```
16−at the type-level (previously only value-level metadata was available).
17
57
18−Examples can be found in the `examples` folder. This library makes heavy use of
58+# Taxonomy of optics
19−[Visible Type Applications](https://ghc.haskell.org/trac/ghc/wiki/TypeApplication).
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.
20
62
21## Lenses
63## Lenses
22
64
23−### Record fields
65+A lens identifies exactly one part of a product type, and allows querying and
66+updating it.
24
67
25−Record fields can be accessed by their label:
68+### By name
26
69
27```haskell
70```haskell
28data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
71data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
@@ -31,42 +74,83 @@ sally :: Person
31sally = Person "Sally" 25
74sally = Person "Sally" 25
32```
75```
33
76
34−```haskell
77+Record fields can be accessed by their label using the `field` lens.
35−>>> getField @"age" sally
36−25
37−
38−>>> setField @"age" 26 sally
39−Person {name = "Sally", age = 26}
40
78
79+```haskell
41>>> sally ^. field @"name"
80>>> sally ^. field @"name"
42"Sally"
81"Sally"
43
82
44>>> sally & field @"name" .~ "Tamas"
83>>> sally & field @"name" .~ "Tamas"
45Person {name = "Tamas", age = 25}
84Person {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.
46
90
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
47>>> sally ^. field @"pet"
102>>> sally ^. field @"pet"
48error:
103error:
49− • The type Person does not contain a field named "pet"
104+ • The type Person does not contain a field named 'pet'
50```
105```
51
106
52−If the accessed field is a type parameter that appears uniquely in the type,
107+For types with multiple constructors, we can still use `field` as long as all constructors contain the required field
53−then its type can be changed:
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+```
54
124
125+The type of `field` is
55```haskell
126```haskell
56−data T a b c d = T
127+field :: HasField name s t a b => Lens s t a b
57− { paramA :: a
128+```
58− , paramB :: b
129+Therefore it allows polymorphic (type-changing) updates, when the accessed field mentions type parameters.
59− , paramC :: c
60− , paramD :: d
61− }
62− deriving (Generic, Show)
63
130
64−>>> t = T "a" (10 :: Int) 'c' False
131+```haskell
65−>>> t & field @"paramA" .~ 'a' & field @"paramB" .~ False
132+data Foo f a = Foo
66−T {paramA = 'a', paramB = False, paramC = 'c', paramD = False}
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)
67```
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!
68
152
69−### Positional fields
153+### By position
70
154
71Fields can be accessed by their position in the data structure (index starting at 1):
155Fields can be accessed by their position in the data structure (index starting at 1):
72
156
@@ -79,12 +163,6 @@ polygon = Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 7 (-2))
79```
163```
80
164
81```haskell
165```haskell
82−>>> getPosition @2 polygon
83−Point 2 4 2
84−
85−>>> setPosition @1 (Point 26 5 3) polygon
86−Polygon (Point 26 5 3) (Point 2 4 2) (Point 5 7 (-2))
87−
88>>> polygon ^. position @1 . position @2
166>>> polygon ^. position @1 . position @2
895
1675
90
168
@@ -96,49 +174,34 @@ error:
96 • The type Polygon does not contain a field at position 10
174 • The type Polygon does not contain a field at position 10
97```
175```
98
176
99−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:
100
178
101```haskell
179```haskell
102>>> (("hello", True), 5) ^. position @1 . position @2
180>>> (("hello", True), 5) ^. position @1 . position @2
103True
181True
182+>>> (("hello", True, "or"), 5, "even", "longer") ^. position @1 . position @2
183+True
104```
184```
105
185
106−### Typed fields
186+### By type
107
187
108Fields can be accessed by their type in the data structure, assuming that this
188Fields can be accessed by their type in the data structure, assuming that this
109type is unique:
189type is unique:
110
190
111```haskell
191```haskell
112−data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
192+>>> sally ^. typed @String
113−data Point = Point Int Int Int deriving (Generic, Show)
114−
115−sally :: Person
116−sally = Person "Sally" 25
117−
118−point :: Point
119−point = Point 1 2 3
120−```
121−
122−```haskell
123−>>> getTyped @String sally
124"Sally"
193"Sally"
125
194
126>>> setTyped @Int sally 26
195>>> setTyped @Int sally 26
127Person {name = "Sally", age = 26}
196Person {name = "Sally", age = 26}
128−
129−>>> point ^. typed @Int
130−error:
131− • The type Point contains multiple values of type Int; the choice of value is thus ambiguous
132−
133−>>> point & typed @String .~ "Point"
134−error:
135− • The type Point does not contain a value of type [Char]
136```
197```
137
198
138−### Structural subtyping
199+### By structure
139
200
201+The `super` lens generalises the `field` lens to focus on a collection rather
202+than a single field.
140−A record is a (structural) `subtype' of another, if its fields are a superset of
203+We can say that a record is a (structural) `subtype' of another, if its fields
141−those of the other.
204+are a superset of those of the other.
142
205
143```haskell
206```haskell
144data Human = Human
207data Human = Human
@@ -157,16 +220,10 @@ human = Human {name = "Tunyasz", age = 50, address = "London"}
157```
220```
158
221
159```haskell
222```haskell
160−
161−>>> upcast human :: Animal
223+>>> human ^. super @Animal
162Animal {name = "Tunyasz", age = 50}
224Animal {name = "Tunyasz", age = 50}
163
225
164−-- 'smash' plug the smaller structure into the larger one
226+>>> upcast human :: Animal
165−>>> smash (Animal "dog" 10) human
166−Human {name = "dog", age = 10, address = "London"}
167−
168−-- 'super' is a lens that focuses on a subrecord of a larger record:
169−>>> human ^. super @Animal
170Animal {name = "Tunyasz", age = 50}
227Animal {name = "Tunyasz", age = 50}
171```
228```
172
229
@@ -181,9 +238,96 @@ growUp (Animal name age) = Animal name (age + 50)
181Human {name = "Tunyasz", age = 60, address = "London"}
238Human {name = "Tunyasz", age = 60, address = "London"}
182```
239```
183
240
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+
184## Prisms
324## Prisms
185
325
186−### 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
187
331
188Constructor components can be accessed using the constructor's name:
332Constructor components can be accessed using the constructor's name:
189
333
@@ -205,19 +349,20 @@ Just (MkDog {name = "Shep", age = 4})
205
349
206>>> shep ^? _Ctor @"Cat"
350>>> shep ^? _Ctor @"Cat"
207Nothing
351Nothing
352+```
353+
354+Constructors with multiple fields can be focused as a tuple
208
355
356+```
209>>> mog ^? _Ctor @"Cat"
357>>> mog ^? _Ctor @"Cat"
210Just ("Mog",5)
358Just ("Mog",5)
211
359
212>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
360>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
213Cat "Garfield" 6
361Cat "Garfield" 6
214
362
215−>>> donald ^? _Ctor @"Giraffe"
216−error:
217− • The type Animal does not contain a constructor named "Giraffe"
218```
363```
219
364
220−### Typed constructors
365+### By type
221
366
222Constructor components can be accessed using the component's type, assuming
367Constructor components can be accessed using the component's type, assuming
223that this type is unique:
368that this type is unique:
@@ -227,10 +372,10 @@ type Name = String
227type Age = Int
372type Age = Int
228
373
229data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
374data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
230−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)
231
376
232shep = Dog (MkDog "Shep" 4)
377shep = Dog (MkDog "Shep" 4)
233−mog = Cat ("Mog", 5)
378+mog = Cat "Mog" 5
234donald = Duck 4
379donald = Duck 4
235```
380```
236
381
@@ -244,6 +389,9 @@ Just (MkDog {name = "Shep", age = 4})
244>>> donald ^? _Typed @Age
389>>> donald ^? _Typed @Age
245Just 4
390Just 4
246
391
392+>>> mog ^? _Typed @(Name, Age)
393+("Mog", 5)
394+
247>>> donald ^? _Typed @Float
395>>> donald ^? _Typed @Float
248error:
396error:
249 • The type Animal does not contain a constructor whose field is of type Float
397 • The type Animal does not contain a constructor whose field is of type Float
@@ -252,7 +400,19 @@ error:
252Duck 6
400Duck 6
253```
401```
254
402
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.
255−## Contributors
414+# Contributors
256
415
416++ [Matthew Pickering](https://github.com/mpickering)
257+ [Toby Shaw](https://github.com/TobyShaw)
417+ [Toby Shaw](https://github.com/TobyShaw)
258+ [Will Jones](https://github.com/lunaris)
418+ [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+35−15
@@ -1,26 +1,32 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE DeriveGeneric #-}
3+{-# LANGUAGE DeriveGeneric #-}
4−{-# LANGUAGE DuplicateRecordFields #-}
4+{-# LANGUAGE DuplicateRecordFields #-}
5−{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE NoMonomorphismRestriction #-}
7+{-# LANGUAGE NoMonomorphismRestriction #-}
8+{-# LANGUAGE OverloadedLabels #-}
8−{-# LANGUAGE PartialTypeSignatures #-}
9+{-# LANGUAGE PartialTypeSignatures #-}
9−{-# LANGUAGE Rank2Types #-}
10+{-# LANGUAGE Rank2Types #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeApplications #-}
12+{-# LANGUAGE TypeApplications #-}
12−{-# LANGUAGE UndecidableInstances #-}
13+{-# LANGUAGE UndecidableInstances #-}
13−{-# OPTIONS_GHC -Wno-missing-signatures #-}
14+{-# OPTIONS_GHC -Wno-missing-signatures #-}
14−{-# OPTIONS_GHC -fno-warn-unused-imports #-}
15+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
15
16
16module Examples where
17module Examples where
17
18
18import Data.Function ((&))
19import Data.Function ((&))
19−import Data.Generics.Internal.Lens
20+import Data.Generics.Internal.VL.Lens
20import Data.Generics.Product
21import Data.Generics.Product
21import Data.Generics.Sum
22import Data.Generics.Sum
22import GHC.Generics
23import GHC.Generics
23import Data.Generics.Labels
24import 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
24
30
25data Animal = Animal
31data Animal = Animal
26 { name :: String
32 { name :: String
@@ -110,3 +116,17 @@ stuff' ::
110 , HasField "bar" a' b' s t
116 , HasField "bar" a' b' s t
111 ) => s' -> t'
117 ) => s' -> t'
112stuff' r = r & #test . #bar . position @15 .~ "hello"
118stuff' 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/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+97−29
@@ -1,7 +1,7 @@
1name: generic-lens
1name: generic-lens
2−version: 0.5.1.0
2+version: 1.2.0.1
3−synopsis: Generic data-structure operations exposed as lenses.
3+synopsis: Generically derive traversals, lenses and prisms.
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.
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.
5
5
6homepage: https://github.com/kcsongor/generic-lens
6homepage: https://github.com/kcsongor/generic-lens
7license: BSD3
7license: BSD3
@@ -11,82 +11,142 @@ maintainer: kiss.csongor.kiss@gmail.com
11category: Generics, Records, Lens
11category: Generics, Records, Lens
12build-type: Simple
12build-type: Simple
13cabal-version: >= 1.10
13cabal-version: >= 1.10
14−Tested-With: GHC == 8.0.2, GHC == 8.2.1
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
15
15
16extra-source-files: ChangeLog.md
16extra-source-files: ChangeLog.md
17 , examples/StarWars.hs
17 , examples/StarWars.hs
18 , examples/Examples.hs
18 , examples/Examples.hs
19 , README.md
19 , README.md
20
20
21+flag dump-core
22+ description: Dump HTML for the core generated by GHC during compilation
23+ default: False
24+
21library
25library
22− exposed-modules: Data.Generics.Product
26+ exposed-modules: Data.Generics.Wrapped
27+ , Data.Generics.Product
23 , Data.Generics.Product.Any
28 , Data.Generics.Product.Any
24 , Data.Generics.Product.Fields
29 , Data.Generics.Product.Fields
30+ , Data.Generics.Product.Param
25 , Data.Generics.Product.Positions
31 , Data.Generics.Product.Positions
26 , Data.Generics.Product.Subtype
32 , Data.Generics.Product.Subtype
27 , Data.Generics.Product.Typed
33 , Data.Generics.Product.Typed
28− , Data.Generics.Labels
29 , Data.Generics.Product.Types
34 , Data.Generics.Product.Types
30− , Data.Generics.Product.Boggle
35+ , Data.Generics.Product.Constraints
31−
36+ , Data.Generics.Product.HList
32− , Boggle
37+ , Data.Generics.Labels
38+ , Data.Generics.Internal.GenericN
33
39
34 , Data.Generics.Sum
40 , Data.Generics.Sum
35 , Data.Generics.Sum.Any
41 , Data.Generics.Sum.Any
36 , Data.Generics.Sum.Constructors
42 , Data.Generics.Sum.Constructors
37 , Data.Generics.Sum.Typed
43 , Data.Generics.Sum.Typed
38 , Data.Generics.Sum.Subtype
44 , Data.Generics.Sum.Subtype
39− , 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
40
54
41 other-modules: Data.Generics.Internal.Families
55 other-modules: Data.Generics.Internal.Families
42 , Data.Generics.Internal.Families.Changing
56 , Data.Generics.Internal.Families.Changing
43 , Data.Generics.Internal.Families.Collect
57 , Data.Generics.Internal.Families.Collect
44 , Data.Generics.Internal.Families.Has
58 , Data.Generics.Internal.Families.Has
45− , Data.Generics.Internal.HList
59+ , Data.Generics.Internal.Errors
46 , Data.Generics.Internal.Void
60 , Data.Generics.Internal.Void
47
61
48 , Data.Generics.Sum.Internal.Constructors
62 , Data.Generics.Sum.Internal.Constructors
49 , Data.Generics.Sum.Internal.Typed
63 , Data.Generics.Sum.Internal.Typed
50 , Data.Generics.Sum.Internal.Subtype
64 , Data.Generics.Sum.Internal.Subtype
51
65
52− , Data.Generics.Product.Internal.Fields
53 , Data.Generics.Product.Internal.Positions
66 , Data.Generics.Product.Internal.Positions
67+ , Data.Generics.Product.Internal.GLens
54 , Data.Generics.Product.Internal.Subtype
68 , Data.Generics.Product.Internal.Subtype
55− , Data.Generics.Product.Internal.Typed
56− , Data.Generics.Product.Internal.Types
57− , Data.Profunctor.Extra
58
69
70+ , Data.Generics.Product.Internal.Constraints
71+ , Data.Generics.Product.Internal.HList
72+
59− build-depends: base >= 4.9 && <= 5.0
73+ build-depends: base >= 4.9 && < 5
60− , profunctors >= 5.0 && <= 6.0
74+ , profunctors >= 5.0 && < 6.0
61− , tagged >= 0.8 && <= 0.9
75+ , tagged >= 0.8 && < 1
76+ , text >= 1.2 && < 1.3
62
77
63 hs-source-dirs: src
78 hs-source-dirs: src
64 default-language: Haskell2010
79 default-language: Haskell2010
65− ghc-options: -Wall -fsimpl-tick-factor=0 -ddump-simpl-stats -ddump-to-file
80+ ghc-options: -Wall
66− if flag(dump-core)
81+-- if flag(dump-core)
67− build-depends: dump-core
82+-- build-depends: dump-core
68− ghc-options: -fplugin=DumpCore -fplugin-opt DumpCore:core-html
83+-- ghc-options: -fplugin=DumpCore -fplugin-opt DumpCore:core-html
69−
70−flag dump-core
71− description: Dump HTML for the core generated by GHC during compilation
72− default: False
73
84
74source-repository head
85source-repository head
75 type: git
86 type: git
76 location: https://github.com/kcsongor/generic-lens
87 location: https://github.com/kcsongor/generic-lens
77
88
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+
78test-suite generic-lens-test
102test-suite generic-lens-test
79 type: exitcode-stdio-1.0
103 type: exitcode-stdio-1.0
80 hs-source-dirs: test
104 hs-source-dirs: test
81 main-is: Spec.hs
105 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
82
141
83 build-depends: base >= 4.9 && <= 5.0
142 build-depends: base >= 4.9 && <= 5.0
84 , generic-lens
143 , generic-lens
144+ , lens
85 , profunctors
145 , profunctors
86− , inspection-testing >= 0.1
146+ , HUnit
87
147
88 default-language: Haskell2010
148 default-language: Haskell2010
89− ghc-options: -Wall -O2
149+ ghc-options: -Wall
90 if flag(dump-core)
150 if flag(dump-core)
91 build-depends: dump-core
151 build-depends: dump-core
92 ghc-options: -fplugin=DumpCore -fplugin-opt DumpCore:core-html
152 ghc-options: -fplugin=DumpCore -fplugin-opt DumpCore:core-html
@@ -99,6 +159,14 @@ test-suite doctests
99 build-depends: base >4 && <5, doctest
159 build-depends: base >4 && <5, doctest
100 hs-source-dirs: test
160 hs-source-dirs: test
101
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
169+
102benchmark generic-lens-bench
170benchmark generic-lens-bench
103 type: exitcode-stdio-1.0
171 type: exitcode-stdio-1.0
104 hs-source-dirs: benchmarks
172 hs-source-dirs: benchmarks
@@ -108,7 +176,7 @@ benchmark generic-lens-bench
108
176
109 , base >= 4.9 && <= 5.0
177 , base >= 4.9 && <= 5.0
110 , QuickCheck
178 , QuickCheck
111− , criterion
179+ , criterion >= 1.3.0.0
112 , deepseq
180 , deepseq
113 , lens
181 , lens
114
182
src/Boggle.hsdeleted−533
@@ -1,533 +0,0 @@
1−{-# LANGUAGE RankNTypes #-}
2−
3−-- | This module implements the 'Boggle' type which exists for its
4−-- 'Applicative' instance that takes advantage of the laws of the
5−-- 'Applicative' class to rearrange the applications of the underlying
6−-- type's 'Applicative' instance. These transformations collect all of
7−-- the pure values using in 'pure' and 'fmap' calls into a single place
8−-- which enables GHC to aggressively optimize them.
9−--
10−-- == Optimization Goals
11−--
12−-- The goal for rewriting values defined in terms of 'Applicative' operations
13−-- will be to normalize them into a form that gives GHC the most opportunity
14−-- to simply the resulting expression as possible without knowing anything
15−-- about the particular 'Applicative' instance that that it satisfies the class
16−-- laws.
17−--
18−-- The following type characterizes our desired normal form.
19−--
20−-- > data Normal :: (* -> *) -> * -> * where
21−-- > Pure :: a -> Normal f a
22−-- > Normal1 :: Normal1 f a -> Normal f a
23−--
24−-- > data Normal1 :: (* -> *) -> * -> * where
25−-- > Ap :: Normal1 f (a -> b) -> f a -> Normal1 f b
26−-- > Normal2 :: Normal2 f a -> Normal1 f a
27−--
28−-- > data Normal2 :: (* -> *) -> * -> * where
29−-- > Fmap :: (a -> b) -> f a -> Normal2 f b -- function ≠ 'id'
30−-- > Lift :: f a -> Normal2 f a
31−--
32−-- While this type on its own is simpler than some of the types that follow,
33−-- implementing '<$>' and '<*>' for this type would require recusion to deal
34−-- with the left-recursion found in this type:
35−--
36−-- > f <$> Ap mg mx = Ap ((f .) <$> mg) mx
37−-- > f <$> Normal2 mx = Normal2 (f <$> x)
38−--
39−-- == Optimization Techniques
40−--
41−-- This transformation uses four primary techniques to achieve optimization.
42−--
43−-- [/avoid recursion/]
44−--
45−-- Recursive functions interfere with GHC's ability to inline and optimize
46−-- function applications. Each of the following transformations will need
47−-- to be written in a way that enables GHC to inline all of the definitions
48−-- and optimize away all of the intermediate structures. If the
49−-- intermediate structures or the operations on them were written
50−-- recursively this would not be possible.
51−--
52−-- [/multiple equivalent representations/]
53−--
54−-- There are cases where we don't know what the optimal representation for
55−-- a value will be until that value is actually used. In the following code
56−-- we see this happen with 'MapK1' and 'ApK1'. Due to inlining and
57−-- optimization the representation that was not needed can be eliminated
58−-- at use time.
59−--
60−-- [/free structures/]
61−--
62−-- In order to optimize values according to laws that they satisfy, we'll
63−-- need the structures that these laws operate over to be explicit. This
64−-- will enable the operations to determine when a transformation is
65−-- appropriate and apply it.
66−--
67−-- 'MapK' tracks the eventual argument to 'fmap'. 'ApK' tracks the eventual
68−-- left-most argument to '<.>'. 'PureK' tracks uses of 'pure'.
69−--
70−-- [/typed tagless final encodings/]
71−--
72−-- The /typed tagless final/ approach for writing a computation involves
73−-- defining the signature of the operations that a compution can be
74−-- constructed from and then defining values completely within that
75−-- signature. This technique will specifically apply to computations
76−-- written generically in terms of the 'Applicative' signature.
77−--
78−-- Because the 'Applicative' signature carries a number of laws that
79−-- any interpretation of it must satisfy, we are able to write our
80−-- own interpretations in terms of existing interpretations but which
81−-- use the laws that these existing interpretations must satisfy.
82−--
83−-- It happens that it is now quite common to define computations in this
84−-- style with classes like 'Functor' and 'Applicative' due to the
85−-- popularlity of the @lens@ package and it's heavy use of this pattern.
86−module Boggle
87− ( Boggle(..)
88− , boggling
89− , test
90− , liftBoggle, lowerBoggle
91− -- * Abstractions
92− , Traversal(..)
93− , Apply(..)
94− , ApWrap(..), liftApWrap, lowerApWrap
95− -- * fmap fusion
96− , MapK(..), (<<$>), liftMapK, lowerMapK
97− -- * fmap fusion with fmap id law
98− , MapK1(..), liftMapK1, lowerMapK1
99− -- * '<*>' reassociation
100− , ApK(..), (<<.>), liftApK, lowerApK
101− -- * '<.>' reassociation
102− , ApK1(..), liftApK1, lowerApK1
103− -- * pure elminination
104− , PureK(..), liftPureK, lowerPureK
105− -- * '>>=' reassociation
106− , BindK(..), liftBindK, lowerBindK, liftBindK1, liftBindK2
107− ) where
108−
109−import Control.Applicative
110−import Control.Monad
111−import GHC.Exts ( inline )
112−
113−infixl 4 <<$>, <<.>, <.>
114−
115−type LensLike f s t a b = (a -> f b) -> (s -> f t)
116−type Traversal s t a b = forall f. Applicative f => LensLike f s t a b
117−type Traversal' s a = Traversal s s a a
118−
119−-- | This class is a mid-point between 'Functor' and 'Applicative'
120−-- for types that support the '<*>' operation but not 'pure'
121−--
122−-- It provides an operation for lifted function application ('<.>')
123−--
124−-- Implementations of this class must follow these laws:
125−--
126−-- [/composition/]
127−--
128−-- * @(\\f g x -> f (g x)) '<$>' mf '<.>' mg '<.>' mx = mf '<.>' (mg '<.>' mx)@
129−--
130−-- [/interchange/]
131−--
132−-- * @(\\g x -> f (g x)) '<$>' mg '<.>' mx = f '<$>' (mg '<.>' mx)@
133−-- * @(\\g x -> g (f x)) '<$>' mg '<.>' mx = mg '<.>' (f '<$>' mx)@
134−--
135−-- If @f@ is an 'Applicative', it should satisfy
136−--
137−-- * @'<.>' = '<*>'@
138−class Functor f => Apply f where
139− -- | Lifted application
140− (<.>) :: f (a -> b) -> f a -> f b
141− {-# MINIMAL (<.>) #-}
142−
143−------------------------------------------------------------------------
144−
145−-- | 'ApWrap' provides an 'Apply' instance in terms of an underlying
146−-- 'Applicative' instance.
147−newtype ApWrap f a = ApWrap (f a)
148−
149−liftApWrap :: f a -> ApWrap f a
150−liftApWrap = ApWrap
151−
152−lowerApWrap :: ApWrap f a -> f a
153−lowerApWrap (ApWrap fa) = fa
154−
155−instance Functor f => Functor (ApWrap f) where
156− fmap f (ApWrap x) = ApWrap (f <$> x)
157−
158−-- | @('<.>') = ('<*>')@
159−instance Applicative f => Apply (ApWrap f) where
160− ApWrap f <.> ApWrap x = ApWrap (f <*> x)
161−
162−
163−------------------------------------------------------------------------
164−
165−-- | This type fuses all uses of 'fmap' into a single use of 'fmap' on
166−-- the underlying 'Functor' @f@.
167−--
168−-- There is a natural isomorphism between @f@ and @'MapK' f@ witnessed by
169−-- 'liftMapK' and 'lowerMapK' which is respected by the 'Functor' instance
170−-- of @'MapK' f@.
171−newtype MapK f a = MapK (forall b. (a -> b) -> f b)
172−
173−liftMapK :: Functor f => f a -> MapK f a
174−liftMapK fa = MapK (<$> fa)
175−
176−lowerMapK :: MapK f a -> f a
177−lowerMapK fa = id <<$> fa
178−
179−-- | Like '<$>' but removes the 'MapK'
180−(<<$>) :: (a -> b) -> MapK f a -> f b
181−f <<$> MapK x = x f
182−
183−-- | Note: no underlying 'Functor' required
184−instance Functor (MapK f) where
185− fmap f x = MapK (\z -> (z . f) <<$> x)
186−
187−------------------------------------------------------------------------
188−
189−-- | 'MapK1' extends 'MapK' to detect when a lift is immediately followed
190−-- by a lower. In this case no 'fmap' will be used at all!
191−--
192−-- There is a natural isomorphism between @f@ and @'MapK1' f@ witnessed by
193−-- 'liftMapK1' and 'lowerMapK1' which is respected by the 'Functor' and
194−-- 'Apply' instance of @'MapK1' f@.
195−data MapK1 f a = MapK1 (f a) (MapK f a)
196− -- ^ Invariant: @(x :: f a) == 'lowerMapK' (y :: 'MapK' f a)
197−
198−liftMapK1 :: Functor f => f a -> MapK1 f a
199−liftMapK1 fa = MapK1 fa (liftMapK fa)
200−
201−lowerMapK1 :: MapK1 f a -> f a
202−lowerMapK1 (MapK1 fa _) = fa
203−
204−-- | Note: no underlying 'Functor' required
205−instance Functor (MapK1 f) where
206− fmap f (MapK1 _ g) = MapK1 (f <<$> g) (f <$> g)
207−
208−instance Apply f => Apply (MapK1 f) where
209− MapK1 f g <.> MapK1 x _ =
210− MapK1 (f <.> x) (MapK (\k -> (\a b -> k (a b)) <<$> g <.> x))
211−
212−------------------------------------------------------------------------
213−
214−-- | 'ApK' provides an 'Apply' instance in terms of the underlying @f@'s
215−-- 'Apply' instance, but left-associates all '<.>'. Lowering this type
216−-- requires an 'Applicative' instance.
217−--
218−-- There is a natural isomorphism between @f@ and @'ApK' f@ witnessed by
219−-- 'liftApK' and 'lowerApK' which is respected by the 'Functor' and
220−-- 'Apply' instance of @'ApK' f@.
221−newtype ApK f a = ApK (forall b. f (a -> b) -> f b)
222−
223−liftApK :: Apply f => f a -> ApK f a
224−liftApK fa = ApK (<.> fa)
225−
226−lowerApK :: Applicative f => ApK f a -> f a
227−lowerApK fa = pure id <<.> fa
228−
229−-- | Like '<.>' but removes 'ApK'
230−(<<.>) :: f (a -> b) -> ApK f a -> f b
231−fa <<.> ApK k = k fa
232−
233−instance Functor f => Functor (ApK f) where
234− fmap f x = ApK (\g -> (\a b -> a (f b)) <$> g <<.> x)
235−
236−-- | Note that this 'Apply' instance only uses the underlying 'Functor'
237−instance Functor f => Apply (ApK f) where
238− f <.> x = ApK (\g -> (.) <$> g <<.> f <<.> x)
239−
240−------------------------------------------------------------------------
241−
242−-- | This type provides an 'Apply' instance in terms of the underlying @f@
243−-- type's 'Apply' instance, but it left-associates all uses of '<.>'
244−--
245−-- There is a natural isomorphism between @f@ and @'ApK1' f@ witnessed by
246−-- 'liftApK1' and 'lowerApK1' which is respected by the 'Functor' and
247−-- 'Apply' instance of @'ApK1' f@.
248−data ApK1 f a = ApK1 (f a) (ApK f a)
249− -- ^ Invariant: @(x :: f a) == 'lowerApK' (y :: 'ApK' f a)@
250−
251−instance Functor f => Functor (ApK1 f) where
252− fmap f (ApK1 x y) = ApK1 (f <$> x) (f <$> y)
253−
254−-- | Note that this 'Apply' instance only uses the underlying 'Functor'
255−instance Functor f => Apply (ApK1 f) where
256− ApK1 fl fr <.> ApK1 _ x = ApK1 (fl <<.> x) (fr <.> x)
257−
258−liftApK1 :: Apply f => f a -> ApK1 f a
259−liftApK1 fa = ApK1 fa (liftApK fa)
260−
261−lowerApK1 :: ApK1 f a -> f a
262−lowerApK1 (ApK1 fa _) = fa
263−
264−------------------------------------------------------------------------
265−
266−-- | 'PureK' lifts a type @f@ having an 'Apply' instance to a type
267−-- having an 'Applicative' instance. The 'Applicative' laws for 'pure'
268−-- are used to rewrite all uses of pure into either a single 'pure' or
269−-- into uses of 'fmap' where possible.
270−--
271−-- There is a natural isomorphism between @f@ and @'PureK' f@ witnessed by
272−-- 'liftPureK' and 'lowerPureK' which is respected by the 'Functor',
273−-- 'Apply', and 'Applicative' instance of @'PureK' f@.
274−data PureK f a = Pure a | Dirty (f a)
275−
276−lowerPureK :: Applicative f => PureK f a -> f a
277−lowerPureK (Pure a) = pure a
278−lowerPureK (Dirty fa) = fa
279−
280−liftPureK :: f a -> PureK f a
281−liftPureK = Dirty
282−
283−instance Functor f => Functor (PureK f) where
284− fmap f (Pure x) = Pure (f x)
285− fmap f (Dirty x) = Dirty (f <$> x)
286−
287−instance Apply f => Apply (PureK f) where
288− Dirty f <.> Dirty x = Dirty (f <.> x)
289− Pure f <.> x = f <$> x
290− f <.> Pure x = ($ x) <$> f
291−
292−-- Note that this 'Applicative' instance only uses the underlying 'Apply'
293−instance Apply f => Applicative (PureK f) where
294− pure = Pure
295− (<*>) = (<.>)
296−
297−-- | Transform the underlying type.
298−natPureK :: (f a -> g a) -> PureK f a -> PureK g a
299−natPureK f (Dirty fa) = Dirty (f fa)
300−natPureK _ (Pure a) = Pure a
301−
302−------------------------------------------------------------------------
303−
304−-- | @'Boggle' f@ is isomorphic to @f@ up to the 'Applicative' laws.
305−-- Uses of '<$>' on this type are combined into a single use of '<$>'
306−-- on the underlying @f@ type. Uses of 'pure' are combined and transformed
307−-- to '<$>' where possible. Uses of '<*>' are reassociated to the left.
308−--
309−-- 'PureK' is on the outside because any use of 'pure' is immediately
310−-- intercepted and translated into 'fmap' when needed. 'ApK1' doesn't
311−-- even have/need an 'Applicative' instance.
312−--
313−-- 'ApK1' is next because it uses the 'Functor' instance from its underlying
314−-- type and we want the 'MapK' layer to intercept and fuse all of those 'fmap'
315−-- uses.
316−--
317−-- 'MapK1' is down toward the bottom of the stack to be able to fuse the uses
318−-- of 'fmap' from all the previous layers into one single use.
319−--
320−-- 'ApWrap' is at the very bottom. It only exists to provide an 'Apply'
321−-- instance to the underlying type @f@.
322−--
323−-- There is a natural isomorphism between @f@ and @'Boggle' f@ witnessed by
324−-- 'liftBoggle' and 'lowerBoggle' which is respected by the 'Functor',
325−-- 'Apply', and 'Applicative' instance of @'Boggle' f@.
326−newtype Boggle f a = Boggle (PureK (ApK1 (MapK1 (ApWrap f))) a)
327−
328−instance Functor (Boggle f) where
329− fmap f (Boggle x) = Boggle (f <$> x)
330− {-# INLINE fmap #-}
331−
332−instance Applicative (Boggle f) where
333− pure x = Boggle (pure x)
334− {-# INLINE pure #-}
335− Boggle x <*> Boggle y = Boggle (x <*> y)
336− {-# INLINE (<*>) #-}
337−
338−liftBoggle :: Applicative f => f a -> Boggle f a
339−liftBoggle = Boggle . liftPureK . liftApK1 . liftMapK1 . liftApWrap
340−{-# INLINE[1] liftBoggle #-}
341−
342−-- | 'lowerBoggle' lowers the 'ApK1' and 'MapK' layers first before lowering
343−-- the 'PureK' layer. This ensures that any 'fmap' uses in the 'PureK' layer
344−-- are intercepted by the 'MapK' layer, but the final (possible) use of 'pure'
345−-- in the case that 'pure' is going to be used will happen in the underlying
346−-- @f@ type!
347−lowerBoggle :: Applicative f => Boggle f a -> f a
348−lowerBoggle
349− = lowerPureK . natPureK (lowerApWrap . lowerMapK1 . lowerApK1) . (\(Boggle b) -> b)
350−{-# INLINE lowerBoggle #-}
351−
352−-- | Optimize a 'Traversal' by fusing the '<$>'s and left-associating the '<*>'s
353−--
354−-- This function will only work well on non-recursive traversals. For an example
355−-- of using this technique on recursive cases see "Data.Traversable.Generic".
356−boggling :: Applicative f => LensLike (Boggle f) s t a b -> LensLike f s t a b
357−boggling l = \f x -> lowerBoggle (l (\x -> (liftBoggle (f x))) x)
358−{-# INLINE boggling #-}
359−
360−
361−------------------------------------------------------------------------
362−
363−-- | Local implementation of @Codensity@ type from @kan-extensions@.
364−-- This type captures the concept of a partially applied '>>=' function.
365−newtype BindK f a = BindK { runBindK :: forall b. (a -> f b) -> f b }
366−
367−instance Functor (BindK f) where
368− fmap = liftM
369−
370−instance Applicative (BindK f) where
371− pure x = BindK $ \k -> k x
372− (<*>) = ap
373−
374−instance Alternative f => Alternative (BindK f) where
375− empty = BindK $ \_ -> empty
376− (<|>) = liftBindK2 (<|>)
377− {-# INLINE (<|>) #-}
378−
379−instance Monad (BindK f) where
380− BindK m >>= f = BindK $ \k -> m $ \a -> runBindK (f a) k
381−
382−instance Alternative f => MonadPlus (BindK f)
383−
384−-- | Run a @'BindK' f@ computation with 'pure' as the final continuation.
385−lowerBindK :: Applicative f => BindK f a -> f a
386−lowerBindK (BindK k) = k pure
387−
388−liftBindK :: Monad f => f a -> BindK f a
389−liftBindK fa = BindK (fa >>=)
390−
391−liftBindK1 :: (forall a. f a -> f a) -> BindK f b -> BindK f b
392−liftBindK1 f (BindK k) = BindK (f . k)
393−
394−liftBindK2 :: (forall a. f a -> f a -> f a) -> BindK f b -> BindK f b -> BindK f b
395−liftBindK2 f (BindK m) (BindK n) = BindK (\k -> f (m k) (n k))
396−
397−traversePair f (a, b) = (,) <$> f a <*> f b
398−
399−test :: Applicative f => (a -> f a) -> (a, a) -> f (a, a)
400−test = boggling traversePair
401−
402−{- example
403−-
404−
405−liftBoggle fa <*> (pure (5+) <*> liftBoggle fb)
406−=
407−((Boggle . liftPureK . liftApK1 . liftMapK1 . liftApWrap) fa)
408− <*>
409− (pure (5+) <*> (Boggle . liftPureK . liftApK1 . liftMapK1 . liftApWrap) fb)
410−=
411− Boggle (
412− Dirty (
413− ApK1 (MapK1 (ApWrap fa) (liftMapK (ApWrap fa))) (liftApK (MapK1 (ApWrap fa) (liftMapK (ApWrap fa))))))
414− <*>
415− ( Boggle (Pure (5+))
416− <*>
417− Boggle (
418− Dirty (
419− ApK1 (MapK1 (ApWrap fb) (liftMapK (ApWrap fb))) (liftApK (MapK1 (ApWrap fb) (liftMapK (ApWrap fb)))))))
420−= Boggle (
421− (Dirty (
422− ApK1 (MapK1 (ApWrap fa) (liftMapK (ApWrap fa))) (liftApK (MapK1 (ApWrap fa) (liftMapK (ApWrap fa)))))))
423− <*> (Pure (5+) <*>
424− (Dirty (
425− ApK1 (MapK1 (ApWrap fb) (liftMapK (ApWrap fb))) (liftApK (MapK1 (ApWrap fb) (liftMapK (ApWrap fb)))))))
426−= {- PureK <$> -}
427− Boggle (
428− (Dirty (
429− ApK1 (MapK1 (ApWrap fa) (liftMapK (ApWrap fa))) (liftApK (MapK1 (ApWrap fa) (liftMapK (ApWrap fa)))))))
430− <.>
431− (Dirty ((+5) <$>
432− ApK1 (MapK1 (ApWrap fb) (liftMapK (ApWrap fb))) (liftApK (MapK1 (ApWrap fb) (liftMapK (ApWrap fb)))))))
433−= {- PureK <.> -}
434− Boggle (
435− (Dirty
436− (
437− ApK1 (MapK1 (ApWrap fa) (liftMapK (ApWrap fa))) (liftApK (MapK1 (ApWrap fa) (liftMapK (ApWrap fa))))))
438− <.>
439− ((+5) <$>
440− ApK1 (MapK1 (ApWrap fb) (liftMapK (ApWrap fb))) (liftApK (MapK1 (ApWrap fb) (liftMapK (ApWrap fb)))))
441−= {- ApK1 <$> -}
442− Boggle (
443− (Dirty
444− (
445− ApK1 (MapK1 (ApWrap fa) (liftMapK (ApWrap fa))) (liftApK (MapK1 (ApWrap fa) (liftMapK (ApWrap fa))))))
446− <.>
447− (
448− ApK1 ((+5) <$> MapK1 (ApWrap fb) (liftMapK (ApWrap fb))) ((+5) <$> liftApK (MapK1 (ApWrap fb) (liftMapK (ApWrap fb)))))
449−= {- ApK1 <.> -}
450−
451− Boggle (
452− (Dirty
453− (ApK1
454−
455− (MapK1 (ApWrap fa) (liftMapK (ApWrap fa)))
456− <<.> ((+5) <$> liftApK (MapK1 (ApWrap fb) (liftMapK (ApWrap fb))))
457− -- This isn't going to be evaluated
458− ((liftApK (MapK1 (ApWrap fa) (liftMapK (ApWrap fa))))))
459− <.> ((+5) <$> liftApK (MapK1 (ApWrap fb) (liftMapK (ApWrap fb)))))
460−= {- Remove unevaled term -}
461− Boggle (
462− (Dirty
463− (ApK1
464− (MapK1 (ApWrap fa) (liftMapK (ApWrap fa)))
465− <<.> ((+5) <$> liftApK (MapK1 (ApWrap fb) (liftMapK (ApWrap fb))))
466− <uneval>
467−= {- liftMapK defn -}
468− Boggle (
469− (Dirty
470− (ApK1
471− (MapK1 (ApWrap fa) (liftMapK (ApWrap fa)))
472− <<.> ((+5) <$> liftApK (MapK1 (ApWrap fb) (MapK (<$> (ApWrap fb))))
473− <uneval>
474−= {- liftApK defn -}
475− Boggle (
476− (Dirty
477− (ApK1
478− (MapK1 (ApWrap fa) (liftMapK (ApWrap fa)))
479− <<.> ((+5) <$> ApK (<.> (MapK1 (ApWrap fb) (MapK (<$> (ApWrap fb))))
480−= {- <$> ApK -}
481− Boggle (
482− (Dirty
483− (ApK1
484− (MapK1 (ApWrap fa) (liftMapK (ApWrap fa)))
485− <<.> (ApK (\g -> (\a b -> a (5 + b)) <$> g <<.>
486− (ApK (<.> (MapK1 (ApWrap fb) (MapK (<$> (ApWrap fb)))))
487−= {- <<.> defn -}
488− Boggle (
489− (Dirty
490− (ApK1
491− (MapK1 (ApWrap fa) (liftMapK (ApWrap fa)))
492− <<.> (ApK (\g -> (\a b -> a (5 + b)) <$>
493− (g <.> (MapK1 (ApWrap fb) (MapK (<$> (ApWrap fb))))))
494−= {- <<.> defn -}
495− Boggle (
496− (Dirty
497− (ApK1
498− (\a b -> a (5 + b)) <$> (
499− (MapK1 (ApWrap fa) (liftMapK (ApWrap fa)))
500− <.> (MapK1 (ApWrap fb) (MapK (<$> (ApWrap fb))))))
501−= {- MapK1 <.> -}
502− Boggle (
503− (Dirty
504− (ApK1
505− (\a b -> a (5 + b)) <$> (
506− (MapK1 (ApWrap fa <.> ApWrap fb)
507− (\k -> (\a b -> k (a b)) <<$> (MapK (<$> (ApWrap fa)))))
508− <.> (ApWrap fb)))))
509−= {- <<$> defn -}
510− Boggle (
511− (Dirty
512− (ApK1
513− (\a b -> a (5 + b)) <$> (
514− (MapK1 (ApWrap fa <.> ApWrap fb)
515− (ApWrap ((\k -> (\a b -> k (a b))) <$> fa <*> fb))))
516−=
517− {- <$> -}
518− Boggle (
519− (Dirty
520− (ApK1
521− MapK1 (
522− (\a b -> a (5 + b)) <$> ApWrap fa <.> ApWrap fb))))
523−-}
524−
525−
526−
527−
528−
529−
530−
531−
532−
533−
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+1−2
@@ -3,14 +3,13 @@
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE KindSignatures #-}
4{-# LANGUAGE KindSignatures #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE TypeApplications #-}
7{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeOperators #-}
7{-# LANGUAGE TypeOperators #-}
9
8
10-----------------------------------------------------------------------------
9-----------------------------------------------------------------------------
11-- |
10-- |
12-- Module : Data.Generics.Internal.Families
11-- Module : Data.Generics.Internal.Families
13−-- Copyright : (C) 2017 Csongor Kiss
12+-- Copyright : (C) 2019 Csongor Kiss
14-- License : BSD3
13-- License : BSD3
15-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
14-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
16-- Stability : experimental
15-- Stability : experimental
src/Data/Generics/Internal/Families/Changing.hs+126−52
@@ -2,14 +2,24 @@
2{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE GADTs #-}
5{-# LANGUAGE PolyKinds #-}
6{-# LANGUAGE PolyKinds #-}
6{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeOperators #-}
8{-# LANGUAGE TypeOperators #-}
8{-# LANGUAGE UndecidableInstances #-}
9{-# LANGUAGE UndecidableInstances #-}
9
10
10−module Data.Generics.Internal.Families.Changing where
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
11
21
12−import GHC.TypeLits (Nat, type (+))
22+import GHC.TypeLits (Nat, type (-), type (+), TypeError, ErrorMessage (..))
13
23
14{-
24{-
15 Note [Changing type parameters]
25 Note [Changing type parameters]
@@ -35,53 +45,117 @@ import GHC.TypeLits (Nat, type (+))
35 If after doing the conversion on @s@, @field@'s type is @(P _ a), then @t@ is
45 If after doing the conversion on @s@, @field@'s type is @(P _ a), then @t@ is
36 @s[b/a]@, otherwise @t ~ s@ and @b ~ a@.
46 @s[b/a]@, otherwise @t ~ s@ and @b ~ a@.
37-}
47-}
38−data P (i :: Nat) a
48+
39−
49+-- `P` can be used in place of any type parameter, which means that it can have
40−type Proxied t = Proxied' t 0
50+-- any kind, not just *, so a data type won't work.
41−
51+-- (this caused https://github.com/kcsongor/generic-lens/issues/23)
42−type family Proxied' (t :: k) (next :: Nat) :: k where
52+-- Instead, we use a matchable type family to wrap any `k` - however, we can no longer directly
43− Proxied' (t a :: k) next = (Proxied' t (next + 1)) (P next a)
53+-- pattern match on `P`, as it's not a type constructor. But we can still take it apart as a polymorphic
44− Proxied' t _ = t
54+-- application form. In order to distinguish between applications of P and other type constructors, we use a tag, `PTag`
45−
55+-- to fake a type constructor.
46−type family UnProxied (t :: k) :: k where
56+data PTag = PTag
47− UnProxied (P _ a) = a
57+type family P :: Nat -> k -> PTag -> k
48− UnProxied (t (P _ a) :: k) = UnProxied t a
58+
49− UnProxied t = t
59+type Indexed t = Indexed' t 0
50−
60+
51−type family Change (t :: k) (target :: Nat) (to :: j) :: k where
61+type family Indexed' (t :: k) (next :: Nat) :: k where
52− Change (P target _) target to = (P target to)
62+ Indexed' (t (a :: j) :: k) next = (Indexed' t (next + 1)) (P next a 'PTag)
53− Change (t (P target _) :: k) target to = t (P target to)
63+ Indexed' t _ = t
54− Change (t a :: k) target to = Change t target to a
64+
55− Change t _ _ = t
65+data Sub where
56−
66+ Sub :: Nat -> k -> Sub
57−type family UnApply (a :: k) :: [*] where
67+
58− UnApply (f x) = x ': UnApply f
59− UnApply x = '[]
60−
61−type family Unify a b :: [(*, *)] where
68+type family Unify (a :: k) (b :: k) :: [Sub] where
62− Unify (P n a') a = '[ '(P n a', a)]
69+ Unify (p n _ 'PTag) a' = '[ 'Sub n a']
63− Unify a b = Zip (UnApply a) (UnApply b)
70+ Unify (a x) (b y) = Unify x y ++ Unify a b
64−
71+ Unify a a = '[]
65−type family PSub (subs :: [(*, *)]) :: [(Nat, *)] where
72+ Unify a b = TypeError
66− PSub '[] = '[]
73+ ( 'Text "Couldn't match type "
67− PSub ('(P n _, b) ': xs) = '(n, b) ': PSub xs
74+ ':<>: 'ShowType a
68− PSub (_ ': xs) = PSub xs
75+ ':<>: 'Text " with "
69−
76+ ':<>: 'ShowType b
77+ )
78+
70−type family Zip (xs :: [k]) (ys :: [l]) :: [(k, l)] where
79+type family (xs :: [k]) ++ (ys :: [k]) :: [k] where
71− Zip '[] '[] = '[]
80+ '[] ++ ys = ys
72− Zip (x ': xs) (y ': ys) = '(x, y) ': Zip xs ys
81+ (x ': xs) ++ ys = x ': (xs ++ ys)
73−
82+
74−type family Infer (s :: *) (a' :: *) (a :: *) (w :: *) :: (*, *) where
83+type family Infer (s :: *) (a' :: *) (b :: *) :: * where
75− Infer s' a' a w
84+ Infer (s a) a' b
76− = Infer' s' a' (PSub (Unify a' a)) w
85+ = ReplaceArgs (s a) (Unify a' b)
77−
86+ Infer s _ _ = s
78−type family Infer' (s :: *) (a' :: *) (subs :: [(Nat, k)]) w :: (*, *) where
87+
79− Infer' s' a' '[ '(p, _)] w
88+--------------------------------------------------------------------------------
80− = '(UnProxied (Change s' p w), UnProxied (Change a' p w))
89+
81− Infer' s' a' _ _
90+-- [TODO]: work this out
82− = '(UnProxied s', UnProxied a')
91+--
83−
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+
84−type family PickTv (a :: k) (b :: k) :: * where
101+type family ReplaceArg (t :: k) (pos :: Nat) (to :: j) :: k where
85− PickTv (P _ _) b = b
102+ ReplaceArg (t a) 0 to = t to
86− PickTv (f (P _ _)) (g b) = b
103+ ReplaceArg (t a) pos to = ReplaceArg t (pos - 1) to a
87− PickTv (f a) (g b) = PickTv f g
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.hs+4−3
@@ -7,7 +7,7 @@
7-----------------------------------------------------------------------------
7-----------------------------------------------------------------------------
8-- |
8-- |
9-- Module : Data.Generics.Internal.Families.Collect
9-- Module : Data.Generics.Internal.Families.Collect
10−-- Copyright : (C) 2017 Csongor Kiss
10+-- Copyright : (C) 2019 Csongor Kiss
11-- License : BSD3
11-- License : BSD3
12-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
12-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
13-- Stability : experimental
13-- Stability : experimental
@@ -28,7 +28,8 @@ import Data.Type.Equality (type (==))
28import GHC.Generics
28import GHC.Generics
29import GHC.TypeLits (Symbol, CmpSymbol)
29import GHC.TypeLits (Symbol, CmpSymbol)
30
30
31−import Data.Generics.Internal.HList
31+import Data.Generics.Product.Internal.HList (type (++))
32+import Data.Generics.Internal.Families.Has (GTypes)
32
33
33data TypeStat
34data TypeStat
34 = TypeStat
35 = TypeStat
@@ -83,7 +84,7 @@ type family CollectPartialType t f :: [Symbol] where
83 CollectPartialType t (l :+: r)
84 CollectPartialType t (l :+: r)
84 = CollectPartialType t l ++ CollectPartialType t r
85 = CollectPartialType t l ++ CollectPartialType t r
85 CollectPartialType t (C1 ('MetaCons ctor _ _) f)
86 CollectPartialType t (C1 ('MetaCons ctor _ _) f)
86− = If (t == ListToTuple (GCollect f)) '[ctor] '[]
87+ = If (t == GTypes f) '[ctor] '[]
87 CollectPartialType t (D1 _ f)
88 CollectPartialType t (D1 _ f)
88 = CollectPartialType t f
89 = CollectPartialType t f
89
90
src/Data/Generics/Internal/Families/Has.hs+87−50
@@ -7,7 +7,7 @@
7-----------------------------------------------------------------------------
7-----------------------------------------------------------------------------
8-- |
8-- |
9-- Module : Data.Generics.Internal.Families.Has
9-- Module : Data.Generics.Internal.Families.Has
10−-- Copyright : (C) 2017 Csongor Kiss
10+-- Copyright : (C) 2019 Csongor Kiss
11-- License : BSD3
11-- License : BSD3
12-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
12-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
13-- Stability : experimental
13-- Stability : experimental
@@ -17,73 +17,100 @@
17module Data.Generics.Internal.Families.Has
17module Data.Generics.Internal.Families.Has
18 ( HasTotalFieldP
18 ( HasTotalFieldP
19 , HasTotalTypeP
19 , HasTotalTypeP
20− , HasPartialTypeTupleP
20+ , HasTotalPositionP
21+ , Pos
22+ , HasPartialTypeP
21 , HasCtorP
23 , HasCtorP
24+ , GTypes
22 ) where
25 ) where
23
26
24−import Data.Type.Bool (type (||), type (&&))
27+import Data.Type.Bool (type (||))
25import Data.Type.Equality (type (==))
28import Data.Type.Equality (type (==))
26import GHC.Generics
29import GHC.Generics
27−import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
30+import GHC.TypeLits (Symbol, Nat)
31+import Data.Kind (Type)
28
32
29−import Data.Generics.Internal.HList
33+import Data.Generics.Product.Internal.HList
30
34
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+
31−type family HasTotalFieldP (field :: Symbol) f :: Bool where
44+type family HasTotalFieldP (field :: Symbol) f :: Maybe Type where
32− HasTotalFieldP field (S1 ('MetaSel ('Just field) _ _ _) _)
45+ HasTotalFieldP field (S1 ('MetaSel ('Just field) _ _ _) (Rec0 t))
33− = 'True
46+ = 'Just t
34 HasTotalFieldP field (l :*: r)
47 HasTotalFieldP field (l :*: r)
35− = HasTotalFieldP field l || HasTotalFieldP field r
48+ = Alt (HasTotalFieldP field l) (HasTotalFieldP field r)
36 HasTotalFieldP field (l :+: r)
49 HasTotalFieldP field (l :+: r)
37− = HasTotalFieldP field l && HasTotalFieldP field r
50+ = Both (HasTotalFieldP field l) (HasTotalFieldP field r)
38 HasTotalFieldP field (S1 _ _)
51 HasTotalFieldP field (S1 _ _)
39− = 'False
52+ = 'Nothing
40 HasTotalFieldP field (C1 _ f)
53 HasTotalFieldP field (C1 _ f)
41 = HasTotalFieldP field f
54 = HasTotalFieldP field f
42 HasTotalFieldP field (D1 _ f)
55 HasTotalFieldP field (D1 _ f)
43 = HasTotalFieldP field f
56 = HasTotalFieldP field f
44− HasTotalFieldP field (Rec0 _)
57+ HasTotalFieldP field (K1 _ _)
45− = 'False
58+ = 'Nothing
46 HasTotalFieldP field U1
59 HasTotalFieldP field U1
47− = 'False
60+ = 'Nothing
48 HasTotalFieldP field V1
61 HasTotalFieldP field V1
49− = 'False
62+ = 'Nothing
50− HasTotalFieldP field f
51− = TypeError
52− ( 'ShowType f
53− ':<>: 'Text " is not a valid GHC.Generics representation type"
54− )
55
63
56−type family HasTotalTypeP a f :: Bool where
64+type family HasTotalTypeP (typ :: Type) f :: Maybe Type where
57− HasTotalTypeP t (S1 meta (Rec0 t))
65+ HasTotalTypeP typ (S1 _ (K1 _ typ))
58− = 'True
66+ = 'Just typ
59− HasTotalTypeP t (l :*: r)
67+ HasTotalTypeP typ (l :*: r)
60− = HasTotalTypeP t l || HasTotalTypeP t r
68+ = Alt (HasTotalTypeP typ l) (HasTotalTypeP typ r)
61− HasTotalTypeP t (l :+: r)
69+ HasTotalTypeP typ (l :+: r)
62− = HasTotalTypeP t l && HasTotalTypeP t r
70+ = Both (HasTotalTypeP typ l) (HasTotalTypeP typ r)
63− HasTotalTypeP t (S1 _ _)
71+ HasTotalTypeP typ (S1 _ _)
64− = 'False
72+ = 'Nothing
65− HasTotalTypeP t (C1 m f)
73+ HasTotalTypeP typ (C1 _ f)
66− = HasTotalTypeP t f
74+ = HasTotalTypeP typ f
67− HasTotalTypeP t (D1 m f)
75+ HasTotalTypeP typ (D1 _ f)
68− = HasTotalTypeP t f
76+ = HasTotalTypeP typ f
69− HasTotalTypeP t (Rec0 _)
77+ HasTotalTypeP typ (K1 _ _)
70− = 'False
78+ = 'Nothing
71− HasTotalTypeP t U1
79+ HasTotalTypeP typ U1
72− = 'False
80+ = 'Nothing
73− HasTotalTypeP t V1
81+ HasTotalTypeP typ V1
74− = 'False
82+ = 'Nothing
75− HasTotalTypeP t f
76− = TypeError
77− ( 'ShowType f
78− ':<>: 'Text " is not a valid GHC.Generics representation type"
79− )
80
83
81−type family HasPartialTypeTupleP a f :: Bool where
84+data Pos (p :: Nat)
82− HasPartialTypeTupleP t (l :+: r)
85+
83− = HasPartialTypeTupleP t l || HasPartialTypeTupleP t r
86+type family HasTotalPositionP (pos :: Nat) f :: Maybe Type where
84− HasPartialTypeTupleP t (C1 m f)
87+ HasTotalPositionP pos (S1 _ (K1 (Pos pos) t))
85− = t == ListToTuple (GCollect f)
88+ = 'Just t
86− HasPartialTypeTupleP 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 _
87 = 'False
114 = 'False
88
115
89type family HasCtorP (ctor :: Symbol) f :: Bool where
116type family HasCtorP (ctor :: Symbol) f :: Bool where
@@ -95,3 +122,13 @@ type family HasCtorP (ctor :: Symbol) f :: Bool where
95 = HasCtorP ctor f
122 = HasCtorP ctor f
96 HasCtorP ctor _
123 HasCtorP ctor _
97 = 'False
124 = '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−251
@@ -1,251 +0,0 @@
1−{-# LANGUAGE LambdaCase #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE Rank2Types #-}
4−{-# LANGUAGE TypeOperators #-}
5−
6−-----------------------------------------------------------------------------
7−-- |
8−-- Module : Data.Generics.Internal.Lens
9−-- Copyright : (C) 2017 Csongor Kiss
10−-- License : BSD3
11−-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
12−-- Stability : experimental
13−-- Portability : non-portable
14−--
15−-- Internal lens helpers. Only exported for Haddock
16−--
17−-----------------------------------------------------------------------------
18−module Data.Generics.Internal.Lens where
19−
20−import Control.Applicative (Const(..))
21−import Data.Functor.Identity (Identity(..))
22−import Data.Monoid (First (..))
23−import Data.Profunctor (Choice(right'), Profunctor(dimap, rmap))
24−import Data.Profunctor.Extra
25−import Data.Profunctor.Unsafe ((#.), (.#))
26−import Data.Tagged
27−import GHC.Generics ((:*:)(..), (:+:)(..), Generic(..), M1(..), Rep)
28−import Boggle hiding (Dirty)
29−import Debug.Trace
30−
31−-- | Type alias for lens
32−type Lens' s a
33− = Lens s s a a
34−
35−type Lens s t a b
36− = forall f. Functor f => (a -> f b) -> s -> f t
37−
38−-- | Type alias for traversal
39−type Traversal' s a
40− = forall f. Applicative f => (a -> f a) -> s -> f s
41−
42−-- | Type alias for prism
43−type Prism s t a b
44− = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)
45−
46−type PrismP s t a b
47− = forall p . (Choice p) => p a b -> p s t
48−
49−type PrismE s t a b
50− = forall p f. (MRight p, MProfunctor p) => p a b -> p s t
51−
52−type Prism' s a
53− = Prism s s a a
54−
55−type Iso' s a
56− = forall p f. (Profunctor p, Functor f) => p a (f a) -> p s (f s)
57−
58−type Iso s t a b
59− = forall p f. (Profunctor p, Functor f) => p a (f b) -> p s (f t)
60−
61−type IsoE s t a b
62− = forall p f. (MProfunctor p) => p a b -> p s t
63−
64−-- | Getting
65−(^.) :: s -> ((a -> Const a a) -> s -> Const a s) -> a
66−s ^. l = getConst (l Const s)
67−infixl 8 ^.
68−
69−-- | Setting
70−set :: ((a -> Identity b) -> s -> Identity t) -> b -> s -> t
71−set l b
72− = runIdentity . l (\_ -> Identity b)
73−
74−infixr 4 .~
75−(.~) :: ((a -> Identity b) -> s -> Identity t) -> b -> s -> t
76−(.~) = set
77−
78−infixl 8 ^?
79−(^?) :: s -> ((a -> Const (First a) a) -> s -> Const (First a) s) -> Maybe a
80−s ^? l = getFirst (fmof l (First #. Just) s)
81− where fmof l' f = getConst #. l' (Const #. f)
82−
83−infixr 8 #
84−(#) :: (Tagged b (Identity b) -> Tagged t (Identity t)) -> b -> t
85−(#) p = runIdentity #. unTagged #. p .# Tagged .# Identity
86−
87−-- | Lens focusing on the first element of a product
88−first :: Lens ((a :*: b) x) ((a' :*: b) x) (a x) (a' x)
89−first f (a :*: b)
90− = fmap (:*: b) (f a)
91−
92−-- | Lens focusing on the second element of a product
93−second :: Lens ((a :*: b) x) ((a :*: b') x) (b x) (b' x)
94−second f (a :*: b)
95− = fmap (a :*:) (f b)
96−
97−left :: Prism ((a :+: c) x) ((b :+: c) x) (a x) (b x)
98−left = prism L1 $ gsum Right (Left . R1)
99−
100−right :: Prism ((a :+: b) x) ((a :+: c) x) (b x) (c x)
101−right = prism R1 $ gsum (Left . L1) Right
102−
103−leftE :: PrismE ((a :+: c) x) ((b :+: c) x) (a x) (b x)
104−leftE = prismE L1 $ gsum Right (Left . R1)
105−
106−rightE :: PrismE ((a :+: b) x) ((a :+: c) x) (b x) (c x)
107−rightE = prismE R1 $ gsum (Left . L1) Right
108−
109−gsum :: (a x -> c) -> (b x -> c) -> ((a :+: b) x) -> c
110−gsum f _ (L1 x) = f x
111−gsum _ g (R1 y) = g y
112−
113−combine :: Lens' (s x) a -> Lens' (t x) a -> Lens' ((s :+: t) x) a
114−combine sa _ f (L1 s) = fmap (\a -> L1 (set sa a s)) (f (s ^. sa))
115−combine _ ta f (R1 t) = fmap (\a -> R1 (set ta a t)) (f (t ^. ta))
116−
117−prismE :: (b -> t) -> (s -> Either t a) -> PrismE s t a b
118−prismE bt seta = mdimap (liftFun seta) (EitherFun id bt) . mright
119−
120−prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
121−prism bt seta = dimap seta (either pure (fmap bt)) . right'
122−
123−-- | A type and its generic representation are isomorphic
124−repIso :: (Generic a, Generic b) => Iso a b (Rep a x) (Rep b x)
125−repIso = dimap from (fmap to)
126−
127−repIsoE :: (Generic a, Generic b) => IsoE a b (Rep a x) (Rep b x)
128−repIsoE = ddimap from to
129−
130−
131−-- | 'M1' is just a wrapper around `f p`
132−--mIso :: Iso' (M1 i c f p) (f p)
133−mIso :: Iso (M1 i c f p) (M1 i c g p) (f p) (g p)
134−mIso = dimap unM1 (fmap M1)
135−
136−-- | 'M1' is just a wrapper around `f p`
137−--mIso :: Iso' (M1 i c f p) (f p)
138−mIsoE :: IsoE (M1 i c f p) (M1 i c g p) (f p) (g p)
139−mIsoE = ddimap unM1 M1
140−
141−-- These are specialised versions of the Isos above. On GHC 8.0.2, having
142−-- these functions eta-expanded allows the optimiser to inline these functions.
143−mLens :: Lens (M1 i c f p) (M1 i c g p) (f p) (g p)
144−mLens f s = mIso f s
145−
146−repLens :: (Generic a, Generic b) => Lens a b (Rep a x) (Rep b x)
147−repLens f s = repIso f s
148−
149−sumIso :: Iso' ((a :+: b) x) (Either (a x) (b x))
150−sumIso = dimap f (fmap t)
151− where f (L1 x) = Left x
152− f (R1 x) = Right x
153− t (Left x) = L1 x
154− t (Right x) = R1 x
155−
156−_Left :: Prism' (Either a c) a
157−_Left = prism Left $ either Right (Left . Right)
158−
159−_Right :: Prism' (Either c a) a
160−_Right = prism Right $ either (Left . Left) Right
161−
162−--------------------------------------------------------------------------------
163−
164−data Coyoneda f b = forall a. Coyoneda (a -> b) (f a)
165−
166−instance Functor (Coyoneda f) where
167− fmap f (Coyoneda g fa)
168− = Coyoneda (f . g) fa
169−
170−inj :: Functor f => Coyoneda f a -> f a
171−inj (Coyoneda f a) = fmap f a
172−
173−proj :: Functor f => f a -> Coyoneda f a
174−proj fa = Coyoneda id fa
175−
176−ravel :: Functor f => ((a -> Coyoneda f b) -> s -> Coyoneda f t) -> (a -> f b) -> (s -> f t)
177−ravel coy f s = inj $ coy (\a -> proj (f a)) s
178−
179−--------------------------------------------------------------------------------
180−
181−-- Pull dimaps to left and rights to right
182−
183−{-
184−instance Choice p => Profunctor (MergeRight p) where
185− dimap f g (MergeRight (Dirty pab)) = MergeRight (DMap f g pab)
186− dimap f g (MergeRight (DMap f' g' pab)) = MergeRight (DMap (f' . f) (g . g') pab)
187− dimap f g (MergeRight (DRight pab)) = MergeRight (DMap f g (right' pab))
188−
189−instance Choice p => Choice (MergeRight p) where
190− right' (MergeRight (Dirty pab)) = MergeRight (DRight pab)
191− right' (MergeRight (DMap f g pab)) = MergeRight (DMap (fmap f) (fmap g) (right' pab))
192− right' (MergeRight (DRight pab)) = MergeRight (DMap assoc' assoc (right' pab))
193− -}
194−{-
195−data PrismBoggle p a b where
196− DRight :: (p x y) -> PrismBoggle p (Either z x) (Either z y)
197− DMap :: (c -> a) -> (b -> d) -> (p a b) -> PrismBoggle p c d
198− Dirty :: p a b -> PrismBoggle p a b
199−
200−instance Choice p => Profunctor (PrismBoggle p) where
201− dimap f g (Dirty pab) = DMap f g pab
202− dimap f g (DMap f' g' pab) = DMap (f' . f) (g . g') pab
203− dimap f g (DRight pab) = DMap f g (right' pab)
204−
205−instance Choice p => Choice (PrismBoggle p) where
206− right' (Dirty pab) = DRight pab
207− right' (DMap f g pab) = DMap (fmap f) (fmap g) (right' pab)
208− right' (DRight pab) = DMap assoc assoc' (right' pab)
209−
210−assoc :: Either a (Either b c) -> Either (Either a b) c
211−assoc e = case e of
212− Left a -> Left (Left a)
213− Right ebc -> case ebc of
214− Left b -> Left (Right b)
215− Right c -> Right c
216−
217−assoc' :: Either (Either a b) c -> Either a (Either b c)
218−assoc' e = case e of
219− Right c -> Right (Right c)
220− Left eab -> case eab of
221− Left a -> Left a
222− Right b -> Right (Left b)
223−
224−projPrism :: p a b -> PrismBoggle p a b
225−projPrism pab = Dirty pab
226−
227−--rightFuse :: Choice p => p a b -> p (Either c (Either d a)) (Either c (Either d b))
228−rightFuse x = injPrism (injPrism (right' (right' (right' (projPrism (projPrism x))))))
229−
230−
231−injPrism :: Choice p => PrismBoggle p a b -> p a b
232−injPrism (Dirty pab) = pab
233−injPrism (DMap f g pab) = dimap f g pab
234−injPrism (DRight pab) = right' pab
235−-}
236−-- We don't need the full power of boggle, we just want to fuse together
237−-- two `fmaps`
238−prismRavel ::
239− (forall p . (MProfunctor p, MRight p) => p a b -> p s t)
240− -> Prism s t a b
241−prismRavel l pab = wrappedProfunctor (lowerFusionStack (ddimap id lowerBoggle (wrappedVLMProfunctor (ravelFusionStack l (WrappedVLMProfunctor (ddimap id (liftBoggle) (liftFusionStack (WrappedProfunctor pab))))))))
242−{-# INLINE prismRavel #-}
243−
244−prismPRavel ::
245− (forall p . (MProfunctor p, MRight p) => p a b -> p s t)
246− -> PrismP s t a b
247−prismPRavel l pab = wrappedProfunctor (ravelFusionStack l ((WrappedProfunctor pab)))
248−{-# INLINE prismPRavel #-}
249−
250−
251−
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.hs+1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE PolyKinds #-}
1module Data.Generics.Internal.Void where
2module Data.Generics.Internal.Void where
2
3
3{-
4{-
src/Data/Generics/Labels.hs+123−17
@@ -1,36 +1,142 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE ConstraintKinds #-}
4+{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
3−{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE PolyKinds #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
5−{-# LANGUAGE TypeApplications #-}
10+{-# LANGUAGE TypeApplications #-}
11+{-# LANGUAGE TypeOperators #-}
6−{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE TypeFamilies #-}
7−{-# LANGUAGE UndecidableInstances #-}
13+{-# LANGUAGE UndecidableInstances #-}
8−{-# OPTIONS_GHC -Wno-orphans #-}
14+{-# OPTIONS_GHC -Wno-orphans #-}
9−
10
15
11--------------------------------------------------------------------------------
16--------------------------------------------------------------------------------
12-- |
17-- |
13-- Module : Data.Generics.Labels
18-- Module : Data.Generics.Labels
14−-- Copyright : (C) 2017 Csongor Kiss
19+-- Copyright : (C) 2019 Csongor Kiss
15−-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
16-- License : BSD3
20-- License : BSD3
21+-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17-- Stability : experimental
22-- Stability : experimental
18-- Portability : non-portable
23-- Portability : non-portable
19--
24--
20−-- Provides an (orphan) IsLabel instance for field lenses.
25+-- Provides an (orphan) IsLabel instance for field lenses and constructor prisms.
21-- Use at your own risk.
26-- Use at your own risk.
22--------------------------------------------------------------------------------
27--------------------------------------------------------------------------------
23
28
24−module Data.Generics.Labels () where
29+module Data.Generics.Labels
30+ ( -- * Orphan IsLabel Instance
31+ -- $sec1
32+ Field(..)
33+ , Field'
34+ , Constructor(..)
35+ , Constructor'
36+ ) where
25
37
26import Data.Generics.Product
38import 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+
27import GHC.OverloadedLabels
47import 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)
28
131
132+instance (Functor f, Field name s t a b) => IsLabelHelper 'False name (->) f s t a b where
133+ labelOutput = fieldLens @name
29
134
30−instance (HasField field s t a b, Functor f, sft ~ (s -> f t)) =>
31− IsLabel field ((a -> f b) -> sft) where
32#if __GLASGOW_HASKELL__ >= 802
135#if __GLASGOW_HASKELL__ >= 802
33− fromLabel = field @field
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
34#else
139#else
35− fromLabel _ = field @field
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
36#endif
142#endif
src/Data/Generics/Product.hs+8−1
@@ -1,7 +1,7 @@
1-----------------------------------------------------------------------------
1-----------------------------------------------------------------------------
2-- |
2-- |
3-- Module : Data.Generics.Product
3-- Module : Data.Generics.Product
4−-- Copyright : (C) 2017 Csongor Kiss
4+-- Copyright : (C) 2019 Csongor Kiss
5-- License : BSD3
5-- License : BSD3
6-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
6-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
7-- Stability : experimental
7-- Stability : experimental
@@ -22,7 +22,11 @@ module Data.Generics.Product
22 , module Data.Generics.Product.Positions
22 , module Data.Generics.Product.Positions
23 , module Data.Generics.Product.Subtype
23 , module Data.Generics.Product.Subtype
24 , module Data.Generics.Product.Typed
24 , module Data.Generics.Product.Typed
25+ , module Data.Generics.Product.HList
26+ -- *Traversals
25 , module Data.Generics.Product.Types
27 , module Data.Generics.Product.Types
28+ , module Data.Generics.Product.Param
29+ , module Data.Generics.Product.Constraints
26 ) where
30 ) where
27
31
28import Data.Generics.Product.Any
32import Data.Generics.Product.Any
@@ -31,3 +35,6 @@ import Data.Generics.Product.Positions
31import Data.Generics.Product.Subtype
35import Data.Generics.Product.Subtype
32import Data.Generics.Product.Typed
36import Data.Generics.Product.Typed
33import Data.Generics.Product.Types
37import 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+4−4
@@ -12,7 +12,7 @@
12-----------------------------------------------------------------------------
12-----------------------------------------------------------------------------
13-- |
13-- |
14-- Module : Data.Generics.Product.Any
14-- Module : Data.Generics.Product.Any
15−-- Copyright : (C) 2017 Csongor Kiss
15+-- Copyright : (C) 2019 Csongor Kiss
16-- License : BSD3
16-- License : BSD3
17-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
18-- Stability : experimental
18-- Stability : experimental
@@ -29,7 +29,7 @@ module Data.Generics.Product.Any
29 HasAny (..)
29 HasAny (..)
30 ) where
30 ) where
31
31
32−import Data.Generics.Internal.Lens
32+import Data.Generics.Internal.VL.Lens
33import Data.Generics.Product.Fields
33import Data.Generics.Product.Fields
34import Data.Generics.Product.Positions
34import Data.Generics.Product.Positions
35import Data.Generics.Product.Typed
35import Data.Generics.Product.Typed
@@ -41,7 +41,7 @@ import Data.Generics.Product.Typed
41-- >>> :set -XDataKinds
41-- >>> :set -XDataKinds
42-- >>> :set -XDeriveGeneric
42-- >>> :set -XDeriveGeneric
43-- >>> import GHC.Generics
43-- >>> import GHC.Generics
44−-- >>> :m +Data.Generics.Internal.Lens
44+-- >>> :m +Data.Generics.Internal.VL.Lens
45-- >>> :{
45-- >>> :{
46-- data Human = Human
46-- data Human = Human
47-- { name :: String
47-- { name :: String
@@ -53,7 +53,7 @@ import Data.Generics.Product.Typed
53-- human = Human "Tunyasz" 50 "London"
53-- human = Human "Tunyasz" 50 "London"
54-- :}
54-- :}
55
55
56−class HasAny (sel :: k) s t a b | s sel k -> a where
56+class HasAny sel s t a b | s sel -> a where
57 -- |A lens that focuses on a part of a product as identified by some
57 -- |A lens that focuses on a part of a product as identified by some
58 -- selector. Currently supported selectors are field names, positions and
58 -- selector. Currently supported selectors are field names, positions and
59 -- unique types. Compatible with the lens package's 'Control.Lens.Lens'
59 -- unique types. Compatible with the lens package's 'Control.Lens.Lens'
src/Data/Generics/Product/Boggle.hsdeleted−83
@@ -1,83 +0,0 @@
1−{-# LANGUAGE TypeOperators #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−
4−-- |
5−-- This module generates implementations of the 'traverse' operation which
6−-- make it possible for GHC to optimize away the GHC.Generics value
7−-- representation.
8−module Data.Generics.Product.Boggle
9− (
10− -- * Generic operations
11− genericTraverse,
12− -- * Implementation details
13− GTraversable(gtraverse)
14− ) where
15−
16−import Boggle
17−import Data.Generics.Internal.Lens
18−import GHC.Generics (Generic1, Rep1, to1, from1, (:*:)(..), (:+:)(..),
19− (:.:)(..),
20− M1(..), K1(..), Rec1(..), Par1(..), U1(..), V1)
21−import GHC.Exts (inline)
22−
23−-- NOTE: genericTraversal an gtraverse must be explicitly marked
24−-- for inlining as they need to inline across module boundaries
25−-- for GHC to optimize away the generics representation. The other
26−-- functions don't *need* to be marked for inlining because GHC
27−-- does figure it out, but it's better to be explicit about our
28−-- intention here than to rely on the optimizer any more than
29−-- we already are.
30−
31−-- | Implementation of 'traverse' for any instance of 'Generic1'.
32−genericTraverse ::
33− (Generic1 t, GTraversable (Rep1 t)) => Traversal (t a) (t b) a b
34−genericTraverse f x = lowerBoggle (to1 <$> gtraverse f (from1 x))
35−{-# INLINE genericTraverse #-}
36−
37−-- | The 'GTraversable' class has a method for traversing a generic
38−-- structure. This function is not quite the same as 'traverse' because
39−-- it uses a particular transformation on the underlying applicative functor.
40−class GTraversable t where
41− gtraverse :: Applicative f => (a -> f b) -> t a -> Boggle f (t b)
42−
43−instance GTraversable f => GTraversable (M1 i c f) where
44− gtraverse f (M1 x) = M1 <$> gtraverse f x
45− {-# INLINE gtraverse #-}
46−
47−instance (GTraversable f, GTraversable g) => GTraversable (f :+: g) where
48− gtraverse f (L1 x) = L1 <$> gtraverse f x
49− gtraverse f (R1 x) = R1 <$> gtraverse f x
50− {-# INLINE gtraverse #-}
51−
52−instance (GTraversable f, GTraversable g) => GTraversable (f :*: g) where
53− gtraverse f (x :*: y) = (:*:) <$> gtraverse f x <*> gtraverse f y
54− {-# INLINE gtraverse #-}
55−
56−instance (Traversable f, GTraversable g) => GTraversable (f :.: g) where
57− gtraverse f (Comp1 x) = Comp1 <$> inline traverse (gtraverse f) x
58− {-# INLINE gtraverse #-}
59−
60−instance GTraversable U1 where
61− gtraverse _ _ = pure U1
62− {-# INLINE gtraverse #-}
63−
64−instance GTraversable V1 where
65− gtraverse _ v = v `seq` error "GTraversal/V1: gtraverse"
66− {-# INLINE gtraverse #-}
67−
68−instance GTraversable (K1 i a) where
69− gtraverse _ (K1 x) = pure (K1 x)
70− {-# INLINE gtraverse #-}
71−
72−instance GTraversable Par1 where
73− gtraverse f (Par1 x) = Par1 <$> liftBoggle (f x)
74− {-# INLINE gtraverse #-}
75−
76−instance Traversable t => GTraversable (Rec1 t) where
77− gtraverse f (Rec1 x) = Rec1 <$> liftBoggle (traverse f x)
78− {-# INLINE gtraverse #-}
79−
80−
81−-------------------------------------------------------------------------------
82−
83−
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+98−25
@@ -1,4 +1,5 @@
1{-# LANGUAGE AllowAmbiguousTypes #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
2{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
@@ -15,7 +16,7 @@
15-----------------------------------------------------------------------------
16-----------------------------------------------------------------------------
16-- |
17-- |
17-- Module : Data.Generics.Product.Fields
18-- Module : Data.Generics.Product.Fields
18−-- Copyright : (C) 2017 Csongor Kiss
19+-- Copyright : (C) 2019 Csongor Kiss
19-- License : BSD3
20-- License : BSD3
20-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21-- Stability : experimental
22-- Stability : experimental
@@ -30,20 +31,23 @@ module Data.Generics.Product.Fields
30
31
31 -- $setup
32 -- $setup
32 HasField (..)
33 HasField (..)
33− , HasField'
34+ , HasField' (..)
35+ , HasField_ (..)
34
36
35 , getField
37 , getField
36 , setField
38 , setField
37 ) where
39 ) where
38
40
39import Data.Generics.Internal.Families
41import Data.Generics.Internal.Families
40−import Data.Generics.Internal.Lens
42+import Data.Generics.Internal.VL.Lens as VL
41import Data.Generics.Internal.Void
43import Data.Generics.Internal.Void
42−import Data.Generics.Product.Internal.Fields
44+import Data.Generics.Product.Internal.GLens
43
45
44import Data.Kind (Constraint, Type)
46import Data.Kind (Constraint, Type)
45import GHC.Generics
47import GHC.Generics
46import GHC.TypeLits (Symbol, ErrorMessage(..), TypeError)
48import GHC.TypeLits (Symbol, ErrorMessage(..), TypeError)
49+import Data.Generics.Internal.Profunctor.Lens as P
50+import Data.Generics.Internal.Errors
47
51
48-- $setup
52-- $setup
49-- == /Running example:/
53-- == /Running example:/
@@ -54,7 +58,7 @@ import GHC.TypeLits (Symbol, ErrorMessage(..), TypeError)
54-- >>> :set -XGADTs
58-- >>> :set -XGADTs
55-- >>> :set -XFlexibleContexts
59-- >>> :set -XFlexibleContexts
56-- >>> import GHC.Generics
60-- >>> import GHC.Generics
57−-- >>> :m +Data.Generics.Internal.Lens
61+-- >>> :m +Data.Generics.Internal.VL.Lens
58-- >>> :m +Data.Function
62-- >>> :m +Data.Function
59-- >>> :{
63-- >>> :{
60-- data Human a
64-- data Human a
@@ -75,7 +79,7 @@ import GHC.TypeLits (Symbol, ErrorMessage(..), TypeError)
75-- :}
79-- :}
76
80
77-- |Records that have a field with a given name.
81-- |Records that have a field with a given name.
78−class HasField (field :: Symbol) s t a b | s field -> a, s field b -> t 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
79 -- |A lens that focuses on a field with a given name. Compatible with the
83 -- |A lens that focuses on a field with a given name. Compatible with the
80 -- lens package's 'Control.Lens.Lens' type.
84 -- lens package's 'Control.Lens.Lens' type.
81 --
85 --
@@ -107,42 +111,108 @@ class HasField (field :: Symbol) s t a b | s field -> a, s field b -> t where
107 -- ... The offending constructors are:
111 -- ... The offending constructors are:
108 -- ... HumanNoAddress
112 -- ... HumanNoAddress
109 -- ...
113 -- ...
110− field :: Lens s t a b
114+ field :: VL.Lens s t a b
111
115
112−type HasField' field s a = HasField field s s a a
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
113
139
114-- |
140-- |
115-- >>> getField @"age" human
141-- >>> getField @"age" human
116-- 50
142-- 50
117−getField :: forall f s a. HasField' f s a => s -> a
143+getField :: forall f a s. HasField' f s a => s -> a
118−getField s = s ^. field @f
144+getField = VL.view (field' @f)
119
145
120-- |
146-- |
121-- >>> setField @"age" 60 human
147-- >>> setField @"age" 60 human
122-- Human {name = "Tunyasz", age = 60, address = "London", other = False}
148-- Human {name = "Tunyasz", age = 60, address = "London", other = False}
123setField :: forall f s a. HasField' f s a => a -> s -> s
149setField :: forall f s a. HasField' f s a => a -> s -> s
124−setField = set (field @f)
150+setField = VL.set (field' @f)
125
151
126−instance -- see Note [Changing type parameters]
152+instance
127 ( Generic s
153 ( Generic s
128 , ErrorUnless field s (CollectField field (Rep s))
154 , ErrorUnless field s (CollectField field (Rep s))
129− , Generic t
155+ , GLens' (HasTotalFieldPSym field) (Rep s) a
130− , s' ~ Proxied s
156+ , Defined (Rep s)
131− , Generic 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]
132− , GHasField' field (Rep s) a
168+ ( HasTotalFieldP field (Rep s) ~~ 'Just a
133− , GHasField' field (Rep s') a'
169+ , HasTotalFieldP field (Rep t) ~~ 'Just b
134− , GHasField field (Rep s) (Rep t) a b
170+ , HasTotalFieldP field (Rep (Indexed s)) ~~ 'Just a'
171+ , HasTotalFieldP field (Rep (Indexed t)) ~~ 'Just b'
172+ , t ~~ Infer s a' b
135− , '(t, b) ~ Infer s' a' a (PickTv a' b)
173+ , s ~~ Infer t b' a
174+ , HasField0 field s t a b
136 ) => HasField field s t a b where
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
137
190
138− field f s = ravel (repLens . gfield @field) f s
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
139
199
140−-- See Note [Uncluttering type signatures]
141−instance {-# OVERLAPPING #-} HasField f (Void2 t a) t a b where
200+instance {-# OVERLAPPING #-} HasField_ f (Void1 a) (Void1 b) a b where
142− field = undefined
201+ field_ = undefined
143
202
144−instance {-# OVERLAPPING #-} HasField f (Void1 a) (Void1 b) a b where
203+instance
145− field = undefined
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 #-}
146
216
147type family ErrorUnless (field :: Symbol) (s :: Type) (stat :: TypeStat) :: Constraint where
217type family ErrorUnless (field :: Symbol) (s :: Type) (stat :: TypeStat) :: Constraint where
148 ErrorUnless field s ('TypeStat _ _ '[])
218 ErrorUnless field s ('TypeStat _ _ '[])
@@ -165,3 +235,6 @@ type family ErrorUnless (field :: Symbol) (s :: Type) (stat :: TypeStat) :: Cons
165
235
166 ErrorUnless _ _ ('TypeStat '[] '[] _)
236 ErrorUnless _ _ ('TypeStat '[] '[] _)
167 = ()
237 = ()
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−77
@@ -1,77 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE KindSignatures #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE Rank2Types #-}
9−{-# LANGUAGE ScopedTypeVariables #-}
10−{-# LANGUAGE TypeApplications #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE TypeOperators #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−
15−-----------------------------------------------------------------------------
16−-- |
17−-- Module : Data.Generics.Product.Internal.Fields
18−-- Copyright : (C) 2017 Csongor Kiss
19−-- License : BSD3
20−-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21−-- Stability : experimental
22−-- Portability : non-portable
23−--
24−-- Derive record field getters and setters generically.
25−--
26−-----------------------------------------------------------------------------
27−
28−module Data.Generics.Product.Internal.Fields
29− ( GHasField (..)
30− , GHasField'
31− ) where
32−
33−import Data.Generics.Internal.Families
34−import Data.Generics.Internal.Lens
35−
36−import Data.Kind (Type)
37−import GHC.Generics
38−import GHC.TypeLits (Symbol)
39−
40−-- |As 'HasField' but over generic representations as defined by
41−-- "GHC.Generics".
42−class GHasField (field :: Symbol) (s :: Type -> Type) (t :: Type -> Type) a b | s field -> a, t field -> b where
43− gfield :: Lens (s x) (t x) a b
44−
45−type GHasField' field s a = GHasField field s s a a
46−
47−instance GProductHasField field l r l' r' a b (HasTotalFieldP field l)
48− => GHasField field (l :*: r) (l' :*: r') a b where
49−
50− gfield = gproductField @field @_ @_ @_ @_ @_ @_ @(HasTotalFieldP field l)
51−
52−instance (GHasField' field r a, GHasField' field l a, GHasField field l l' a b, GHasField field r r' a b)
53− => GHasField field (l :+: r) (l' :+: r') a b where
54−
55− gfield f (L1 s) = fmap (\a -> L1 (set (gfield @field) a s)) (f (s ^. gfield @field))
56− gfield f (R1 t) = fmap (\a -> R1 (set (gfield @field) a t)) (f (t ^. gfield @field))
57−
58−instance GHasField field (K1 R a) (K1 R b) a b where
59− gfield f (K1 x) = fmap K1 (f x)
60−
61−instance GHasField field (S1 ('MetaSel ('Just field) upkd str infstr) (Rec0 a)) (S1 ('MetaSel ('Just field) upkd str infstr) (Rec0 b)) a b where
62− gfield = mLens . gfield @field
63−
64−instance (Functor g, GHasField field f g a b) => GHasField field (M1 D meta f) (M1 D meta g) a b where
65− gfield = mLens . gfield @field
66−
67−instance GHasField field f g a b => GHasField field (M1 C meta f) (M1 C meta g) a b where
68− gfield = mLens . gfield @field
69−
70−class GProductHasField (field :: Symbol) l r l' r' a b (left :: Bool) | field l r -> a, field l' r' -> b where
71− gproductField :: Lens ((l :*: r) x) ((l' :*: r') x) a b
72−
73−instance GHasField field l l' a b => GProductHasField field l r l' r a b 'True where
74− gproductField = first . gfield @field
75−
76−instance GHasField field r r' a b => GProductHasField field l r l r' a b 'False where
77− 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 #-}
2{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE PolyKinds #-}
8{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TupleSections #-}
9+{-# LANGUAGE TypeApplications #-}
9{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeInType #-}
10{-# LANGUAGE TypeOperators #-}
12{-# LANGUAGE TypeOperators #-}
11{-# LANGUAGE UndecidableInstances #-}
13{-# LANGUAGE UndecidableInstances #-}
12
14
15+#if __GLASGOW_HASKELL__ == 802
16+{-# OPTIONS_GHC -fno-solve-constant-dicts #-}
17+#endif
18+
19+
13-----------------------------------------------------------------------------
20-----------------------------------------------------------------------------
14-- |
21-- |
15−-- Module : Data.Generics.Internal.HList
22+-- Module : Data.Generics.Product.Internal.HList
16−-- Copyright : (C) 2017 Csongor Kiss
23+-- Copyright : (C) 2019 Csongor Kiss
17-- License : BSD3
24-- License : BSD3
18-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
25-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
19-- Stability : experimental
26-- Stability : experimental
20-- Portability : non-portable
27-- Portability : non-portable
21--
28--
29+-- Derive an isomorphism between a product type and a flat HList.
30+--
22-----------------------------------------------------------------------------
31-----------------------------------------------------------------------------
23−module Data.Generics.Internal.HList
24− ( type (++)
25
32
33+module Data.Generics.Product.Internal.HList
34+ ( GIsList(..)
35+ , IndexList (..)
36+ , HList (..)
37+ , type (++)
38+ , Elem
26 , ListTuple (..)
39 , ListTuple (..)
27−
40+ , TupleToList
28− , GCollectible (..)
29 ) where
41 ) where
30
42
43+#if __GLASGOW_HASKELL__ < 804
44+import Data.Semigroup
45+#endif
46+import GHC.TypeLits
47+
31−import Data.Kind (Type)
48+import Data.Kind (Type)
32import GHC.Generics
49import GHC.Generics
50+import Data.Profunctor
51+import Data.Generics.Internal.Profunctor.Lens
52+import Data.Generics.Internal.Profunctor.Iso
33
53
34−data HList (xs :: [Type]) where
54+data HList (as :: [Type]) where
35− Nil :: HList '[]
55+ Nil :: HList '[]
36− (:>) :: x -> HList xs -> HList (x ': xs)
56+ (:>) :: a -> HList as -> HList (a ': as)
37
57
38infixr 5 :>
58infixr 5 :>
39
59
@@ -41,32 +61,118 @@ type family ((as :: [k]) ++ (bs :: [k])) :: [k] where
41 '[] ++ bs = bs
61 '[] ++ bs = bs
42 (a ': as) ++ bs = a ': as ++ bs
62 (a ': as) ++ bs = a ': as ++ bs
43
63
44−append :: HList as -> HList bs -> HList (as ++ bs)
64+instance Semigroup (HList '[]) where
45−append Nil ys = ys
65+ _ <> _ = Nil
46−append (x :> xs) ys = x :> append xs ys
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
47
103
48−head' :: HList (a ': as) -> a
104+ glist = prodIso . pairing glist glist . appending
49−head' (x :> _) = x
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 #-}
50
118
51--------------------------------------------------------------------------------
119--------------------------------------------------------------------------------
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)
53
142
54−class Splittable (as :: [Type]) (bs :: [Type]) (cs :: [Type]) | as bs -> cs, as cs -> bs where
143+singleton :: Iso a b (HList '[a]) (HList '[ b])
55− split :: HList cs -> (HList as, HList bs)
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
56
152
57−instance Splittable '[] bs bs where
153+instance {-# OVERLAPPING #-}
58− split bs = (Nil, bs)
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 #-}
59
159
60−instance Splittable as bs cs => Splittable (a ': as) bs (a ': cs) where
160+instance
61− split (a :> as)
161+ ( IndexList (n - 1) as' bs' a b
62− = (a :> as', bs)
162+ , as ~ (x ': as')
63− where (as', bs) = split 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 #-}
64
167
65--------------------------------------------------------------------------------
168--------------------------------------------------------------------------------
66-- * Convert tuples to/from HLists
169-- * Convert tuples to/from HLists
67
170
68class ListTuple (tuple :: Type) (as :: [Type]) | as -> tuple where
171class ListTuple (tuple :: Type) (as :: [Type]) | as -> tuple where
69 type ListToTuple as :: Type
172 type ListToTuple as :: Type
173+ tupled :: Iso' (HList as) tuple
174+ tupled = iso listToTuple tupleToList
175+
70 tupleToList :: tuple -> HList as
176 tupleToList :: tuple -> HList as
71 listToTuple :: HList as -> tuple
177 listToTuple :: HList as -> tuple
72
178
@@ -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
201 listToTuple (a :> b :> c :> d :> e :> f :> g :> h :> j :> k :> l :> m :> n :> o :> p :> q :> r :> s :> Nil)
307 listToTuple (a :> b :> c :> d :> e :> f :> g :> h :> j :> k :> l :> m :> n :> o :> p :> q :> r :> s :> Nil)
202 = (a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q, r, s)
308 = (a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q, r, s)
203
309
204−--------------------------------------------------------------------------------
310+type family TupleToList a where
205−
311+ TupleToList () = '[]
206−class GCollectible (f :: Type -> Type) (as :: [Type]) | f -> as where
312+ TupleToList (a, b) = '[a, b]
207− type GCollect f :: [Type]
313+ TupleToList (a, b, c) = '[a, b, c]
208− gtoCollection :: f x -> HList as
314+ TupleToList (a, b, c, d) = '[a, b, c, d]
209− gfromCollection :: HList as -> f x
315+ TupleToList (a, b, c, d, e) = '[a, b, c, d, e]
210−
316+ TupleToList (a, b, c, d, e, f) = '[a, b, c, d, e, f]
211−instance
317+ TupleToList (a, b, c, d, e, f, g) = '[a, b, c, d, e, f, g]
212− ( GCollectible l as
318+ TupleToList (a, b, c, d, e, f, g, h) = '[a, b, c, d, e, f, g, h]
213− , GCollectible r bs
319+ TupleToList (a, b, c, d, e, f, g, h, j) = '[a, b, c, d, e, f, g, h, j]
214− , cs ~ (as ++ bs)
320+ TupleToList (a, b, c, d, e, f, g, h, j, k) = '[a, b, c, d, e, f, g, h, j, k]
215− , Splittable as bs cs
321+ TupleToList (a, b, c, d, e, f, g, h, j, k, l) = '[a, b, c, d, e, f, g, h, j, k, l]
216− ) => GCollectible (l :*: r) cs where
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]
217− type GCollect (l :*: r) = GCollect l ++ GCollect r
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]
218−
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]
219− gtoCollection (l :*: r)
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]
220− = gtoCollection l `append` gtoCollection r
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]
221−
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]
222− gfromCollection cs
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]
223− = gfromCollection as :*: gfromCollection bs
329+ TupleToList a = '[a]
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
src/Data/Generics/Product/Internal/Positions.hs+45−33
@@ -1,3 +1,4 @@
1+{-# LANGUAGE TypeInType #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE DataKinds #-}
@@ -16,7 +17,7 @@
16-----------------------------------------------------------------------------
17-----------------------------------------------------------------------------
17-- |
18-- |
18-- Module : Data.Generics.Product.Internal.Positions
19-- Module : Data.Generics.Product.Internal.Positions
19−-- Copyright : (C) 2017 Csongor Kiss
20+-- Copyright : (C) 2019 Csongor Kiss
20-- License : BSD3
21-- License : BSD3
21-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
22-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
22-- Stability : experimental
23-- Stability : experimental
@@ -27,48 +28,61 @@
27-----------------------------------------------------------------------------
28-----------------------------------------------------------------------------
28
29
29module Data.Generics.Product.Internal.Positions
30module Data.Generics.Product.Internal.Positions
30− ( GHasPosition (..)
31+ ( type (<?)
31− , GHasPosition'
32− , type (<?)
33 , Size
32 , Size
33+ , CRep
34 ) where
34 ) where
35
35
36−import Data.Generics.Internal.Lens
36+import Data.Generics.Internal.Families.Has (Pos)
37−
38import Data.Kind (Type)
37import Data.Kind (Type)
39import Data.Type.Bool (If, Not)
38import Data.Type.Bool (If, Not)
40import GHC.Generics
39import GHC.Generics
41import GHC.TypeLits (type (<=?), type (+), Nat)
40import GHC.TypeLits (type (<=?), type (+), Nat)
42
41
43−-- |As 'HasPosition' but over generic representations as defined by
42+-- | Alias for the kind of the generic rep
44−-- "GHC.Generics".
43+type G = Type -> Type
45−class GHasPosition (offset :: Nat) (i :: Nat) (s :: Type -> Type) (t :: Type -> Type) a b | s offset i -> a, s t offset i -> b where
46− gposition :: Lens (s x) (t x) a b
47−
48−type GHasPosition' i s a = GHasPosition 1 i s s a a
49−
50−instance GHasPosition i i (K1 R a) (K1 R b) a b where
51− gposition f (K1 x) = fmap K1 (f x)
52
44
53−instance GHasPosition offset i s t a b => GHasPosition offset i (M1 m meta s) (M1 m meta t) a b where
45+--------------------------------------------------------------------------------
54− gposition = mLens . gposition @offset @i
55−
56−instance
57− ( goLeft ~ (i <? (offset + Size l))
58− , offset' ~ (If goLeft offset (offset + Size l))
59− , GProductHasPosition offset' i l r l' r' a b goLeft
60− ) => GHasPosition offset i (l :*: r) (l' :*: r') a b where
61−
62− gposition = gproductPosition @offset' @i @_ @_ @_ @_ @_ @_ @goLeft
63
46
64−class GProductHasPosition (offset :: Nat) (i :: Nat) l r l' r' a b (left :: Bool) | offset i l r -> a, offset i l r l' r' -> b where
47+-- | In-order labeling of the generic tree with the field positions
65− gproductPosition :: Lens ((l :*: r) x) ((l' :*: r') x) a b
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)
66
80
67−instance GHasPosition offset i l l' a b => GProductHasPosition offset i l r l' r a b 'True where
81+--------------------------------------------------------------------------------
68− gproductPosition = first . gposition @offset @i
82+-- Utilities
69
83
70−instance GHasPosition offset i r r' a b => GProductHasPosition offset i l r l r' a b 'False where
84+type family Fst (p :: (a, b)) :: a where
71− gproductPosition = second . gposition @offset @i
85+ Fst '(a, b) = a
72
86
73type family Size f :: Nat where
87type family Size f :: Nat where
74 Size (l :*: r)
88 Size (l :*: r)
@@ -82,8 +96,6 @@ type family Size f :: Nat where
82 Size f
96 Size f
83 = 1
97 = 1
84
98
85−--------------------------------------------------------------------------------
86−
87type x <? y = Not (y <=? x)
99type x <? y = Not (y <=? x)
88infixl 4 <?
100infixl 4 <?
89
101
src/Data/Generics/Product/Internal/Subtype.hs+15−10
@@ -1,3 +1,4 @@
1+{-# LANGUAGE TypeInType #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
@@ -15,7 +16,7 @@
15-----------------------------------------------------------------------------
16-----------------------------------------------------------------------------
16-- |
17-- |
17-- Module : Data.Generics.Product.Internal.Subtype
18-- Module : Data.Generics.Product.Internal.Subtype
18−-- Copyright : (C) 2017 Csongor Kiss
19+-- Copyright : (C) 2019 Csongor Kiss
19-- License : BSD3
20-- License : BSD3
20-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21-- Stability : experimental
22-- Stability : experimental
@@ -31,11 +32,12 @@ module Data.Generics.Product.Internal.Subtype
31 ) where
32 ) where
32
33
33import Data.Generics.Internal.Families
34import Data.Generics.Internal.Families
34−import Data.Generics.Internal.Lens
35+import Data.Generics.Product.Internal.GLens
35−import Data.Generics.Product.Internal.Fields
36
36
37import Data.Kind (Type)
37import Data.Kind (Type)
38import GHC.Generics
38import GHC.Generics
39+import GHC.TypeLits (Symbol)
40+import Data.Generics.Internal.Profunctor.Lens (view)
39
41
40--------------------------------------------------------------------------------
42--------------------------------------------------------------------------------
41-- * Generic upcasting
43-- * Generic upcasting
@@ -47,10 +49,10 @@ instance (GUpcast sub a, GUpcast sub b) => GUpcast sub (a :*: b) where
47 gupcast rep = gupcast rep :*: gupcast rep
49 gupcast rep = gupcast rep :*: gupcast rep
48
50
49instance
51instance
50− GHasField field sub sub t t
52+ GLens' (HasTotalFieldPSym field) sub t
51 => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
53 => GUpcast sub (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) where
52
54
53− gupcast r = M1 (K1 (r ^. gfield @field))
55+ gupcast r = M1 (K1 (view (glens @(HasTotalFieldPSym field)) r))
54
56
55instance GUpcast sub sup => GUpcast sub (C1 c sup) where
57instance GUpcast sub sup => GUpcast sub (C1 c sup) where
56 gupcast = M1 . gupcast
58 gupcast = M1 . gupcast
@@ -80,13 +82,16 @@ instance GSmash sub sup => GSmash (C1 c sub) sup where
80instance GSmash sub sup => GSmash (D1 c sub) sup where
82instance GSmash sub sup => GSmash (D1 c sub) sup where
81 gsmash sup (M1 sub) = M1 (gsmash sup sub)
83 gsmash sup (M1 sub) = M1 (gsmash sup sub)
82
84
83−class GSmashLeaf sub sup (w :: Bool) where
85+class GSmashLeaf sub sup (w :: Maybe Type) where
84 gsmashLeaf :: sup p -> sub p -> sub p
86 gsmashLeaf :: sup p -> sub p -> sub p
85
87
86instance
88instance
87− GHasField field sup sup t t
89+ GLens' (HasTotalFieldPSym field) sup t
88− => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup 'True where
90+ => GSmashLeaf (S1 ('MetaSel ('Just field) p f b) (Rec0 t)) sup ('Just t) where
89− gsmashLeaf sup _ = M1 (K1 (sup ^. gfield @field))
91+ gsmashLeaf sup _ = M1 (K1 (view (glens @(HasTotalFieldPSym field)) sup))
90
92
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
92 gsmashLeaf _ = id
94 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 = mLens . 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/Internal/Types.hsdeleted−60
@@ -1,60 +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.Types
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 traversals of a given type in a product.
23−--
24−-----------------------------------------------------------------------------
25−
26−module Data.Generics.Product.Internal.Types
27− ( GHasTypes (..)
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 Boggle
36−
37−-- |As 'HasTypes' but over generic representations as defined by
38−-- "GHC.Generics".
39−class GHasTypes (f :: Type -> Type) a where
40− gtypes :: forall g . Applicative g => (a -> g a) -> f a -> Boggle g (f a)
41−
42−instance (GHasTypes l a, GHasTypes r a) => GHasTypes (l :*: r) a where
43− gtypes f (l :*: r) = (:*:) <$> gtypes f l <*> gtypes f r
44− {-# INLINE gtypes #-}
45−
46−-- TODO:
47−-- instance (GHasTypes l a, GHasTypes r a) => GHasTypes (l :+: r) a where
48−
49−instance GHasTypes (K1 R a) a where
50− gtypes f (K1 x) = fmap K1 (liftBoggle (f x))
51− {-# INLINE gtypes #-}
52−
53−
54−instance {-# OVERLAPS #-} GHasTypes (K1 R a) b where
55− gtypes _ k = pure k
56− {-# INLINE gtypes #-}
57−
58−instance GHasTypes f a => GHasTypes (M1 m meta f) a where
59− gtypes f (M1 x) = M1 <$> gtypes f x
60− {-# INLINE gtypes #-}
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+127−20
@@ -1,3 +1,6 @@
1+{-# LANGUAGE TypeInType #-}
2+{-# LANGUAGE PartialTypeSignatures #-}
3+{-# LANGUAGE CPP #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
4{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE ConstraintKinds #-}
5{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE DataKinds #-}
6{-# LANGUAGE DataKinds #-}
@@ -16,7 +19,7 @@
16-----------------------------------------------------------------------------
19-----------------------------------------------------------------------------
17-- |
20-- |
18-- Module : Data.Generics.Product.Positions
21-- Module : Data.Generics.Product.Positions
19−-- Copyright : (C) 2017 Csongor Kiss
22+-- Copyright : (C) 2019 Csongor Kiss
20-- License : BSD3
23-- License : BSD3
21-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
24-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
22-- Stability : experimental
25-- Stability : experimental
@@ -31,21 +34,27 @@ module Data.Generics.Product.Positions
31
34
32 -- $setup
35 -- $setup
33 HasPosition (..)
36 HasPosition (..)
34− , HasPosition'
37+ , HasPosition' (..)
38+ , HasPosition_ (..)
39+ , HasPosition0 (..)
35
40
36 , getPosition
41 , getPosition
37 , setPosition
42 , setPosition
38 ) where
43 ) where
39
44
40−import Data.Generics.Internal.Lens
45+import Data.Generics.Internal.VL.Lens as VL
41import Data.Generics.Internal.Void
46import Data.Generics.Internal.Void
42import Data.Generics.Internal.Families
47import Data.Generics.Internal.Families
43import Data.Generics.Product.Internal.Positions
48import Data.Generics.Product.Internal.Positions
49+import Data.Generics.Product.Internal.GLens
50+import Data.Generics.Internal.Errors
44
51
45import Data.Kind (Constraint, Type)
52import Data.Kind (Constraint, Type)
46import Data.Type.Bool (type (&&))
53import Data.Type.Bool (type (&&))
47import GHC.Generics
54import GHC.Generics
48import GHC.TypeLits (type (<=?), Nat, TypeError, ErrorMessage(..))
55import GHC.TypeLits (type (<=?), Nat, TypeError, ErrorMessage(..))
56+import Data.Generics.Internal.Profunctor.Lens as P
57+import Data.Coerce
49
58
50-- $setup
59-- $setup
51-- == /Running example:/
60-- == /Running example:/
@@ -56,7 +65,7 @@ import GHC.TypeLits (type (<=?), Nat, TypeError, ErrorMessage(..))
56-- >>> :set -XGADTs
65-- >>> :set -XGADTs
57-- >>> :set -XFlexibleContexts
66-- >>> :set -XFlexibleContexts
58-- >>> import GHC.Generics
67-- >>> import GHC.Generics
59−-- >>> :m +Data.Generics.Internal.Lens
68+-- >>> :m +Data.Generics.Internal.VL.Lens
60-- >>> :m +Data.Function
69-- >>> :m +Data.Function
61-- >>> :{
70-- >>> :{
62-- data Human = Human
71-- data Human = Human
@@ -70,7 +79,7 @@ import GHC.TypeLits (type (<=?), Nat, TypeError, ErrorMessage(..))
70-- :}
79-- :}
71
80
72-- |Records that have a field at a given position.
81-- |Records that have a field at a given position.
73−class HasPosition (i :: Nat) s t a b | s i -> a, s i b -> t 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
74 -- |A lens that focuses on a field at a given position. Compatible with the
83 -- |A lens that focuses on a field at a given position. Compatible with the
75 -- lens package's 'Control.Lens.Lens' type.
84 -- lens package's 'Control.Lens.Lens' type.
76 --
85 --
@@ -85,33 +94,128 @@ class HasPosition (i :: Nat) s t a b | s i -> a, s i b -> t where
85 -- ...
94 -- ...
86 -- ... The type Human does not contain a field at position 4
95 -- ... The type Human does not contain a field at position 4
87 -- ...
96 -- ...
88− position :: Lens s t a b
97+ position :: VL.Lens s t a b
89
98
90−type HasPosition' i s a = HasPosition i s s a a
99+class HasPosition_ (i :: Nat) s t a b where
100+ position_ :: VL.Lens s t a b
91
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
110+
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
92getPosition :: forall i s a. HasPosition' i s a => s -> a
121getPosition :: forall i s a. HasPosition' i s a => s -> a
93−getPosition s = s ^. position @i
122+getPosition s = s ^. position' @i
94
123
124+-- |
125+-- >>> setPosition @2 60 human
126+-- Human {name = "Tunyasz", age = 60, address = "London"}
95setPosition :: forall i s a. HasPosition' i s a => a -> s -> s
127setPosition :: forall i s a. HasPosition' i s a => a -> s -> s
96−setPosition = set (position @i)
128+setPosition = VL.set (position' @i)
97
129
98−instance -- see Note [Changing type parameters]
130+instance
99 ( Generic s
131 ( Generic s
100 , ErrorUnless i s (0 <? i && i <=? Size (Rep s))
132 , ErrorUnless i s (0 <? i && i <=? Size (Rep s))
101− , Generic t
133+ , cs ~ CRep s
102− , s' ~ Proxied s
134+ , Coercible (Rep s) cs
103− , GHasPosition' i (Rep s) a
135+ , GLens' (HasTotalPositionPSym i) cs a
104− , GHasPosition' i (Rep s') a'
136+ , Defined (Rep s)
105− , GHasPosition 1 i (Rep s) (Rep t) a b
137+ (NoGeneric s '[ 'Text "arising from a generic lens focusing on the field at"
106− , '(t, b) ~ Infer s' a' a (PickTv a' b)
138+ , 'Text "position " ':<>: QuoteType i ':<>: 'Text " of type " ':<>: QuoteType a
107− ) => HasPosition i s t a b where
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
108
150
109− position f s = ravel (repLens . gposition @1 @i) f s
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
110
163
111−-- See Note [Uncluttering type signatures]
164+ position = position0 @i
112−instance {-# OVERLAPPING #-} HasPosition f (Void2 a t) t a b where
165+ {-# INLINE position #-}
166+
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
113 position = undefined
186 position = undefined
114
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 #-}
218+
115type family ErrorUnless (i :: Nat) (s :: Type) (hasP :: Bool) :: Constraint where
219type family ErrorUnless (i :: Nat) (s :: Type) (hasP :: Bool) :: Constraint where
116 ErrorUnless i s 'False
220 ErrorUnless i s 'False
117 = TypeError
221 = TypeError
@@ -123,3 +227,6 @@ type family ErrorUnless (i :: Nat) (s :: Type) (hasP :: Bool) :: Constraint wher
123
227
124 ErrorUnless _ _ 'True
228 ErrorUnless _ _ 'True
125 = ()
229 = ()
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+50−9
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
@@ -15,7 +16,7 @@
15-----------------------------------------------------------------------------
16-----------------------------------------------------------------------------
16-- |
17-- |
17-- Module : Data.Generics.Product.Subtype
18-- Module : Data.Generics.Product.Subtype
18−-- Copyright : (C) 2017 Csongor Kiss
19+-- Copyright : (C) 2019 Csongor Kiss
19-- License : BSD3
20-- License : BSD3
20-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21-- Stability : experimental
22-- Stability : experimental
@@ -33,13 +34,15 @@ module Data.Generics.Product.Subtype
33 ) where
34 ) where
34
35
35import Data.Generics.Internal.Families
36import Data.Generics.Internal.Families
36−import Data.Generics.Internal.Lens
37+import Data.Generics.Internal.VL.Lens as VL
37import Data.Generics.Internal.Void
38import Data.Generics.Internal.Void
38import Data.Generics.Product.Internal.Subtype
39import Data.Generics.Product.Internal.Subtype
39
40
40import GHC.Generics (Generic (Rep, to, from) )
41import GHC.Generics (Generic (Rep, to, from) )
41import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
42import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
42import Data.Kind (Type, Constraint)
43import Data.Kind (Type, Constraint)
44+import Data.Generics.Internal.Profunctor.Lens hiding (set)
45+import Data.Generics.Internal.Errors
43
46
44-- $setup
47-- $setup
45-- == /Running example:/
48-- == /Running example:/
@@ -49,7 +52,7 @@ import Data.Kind (Type, Constraint)
49-- >>> :set -XDeriveGeneric
52-- >>> :set -XDeriveGeneric
50-- >>> :set -XDuplicateRecordFields
53-- >>> :set -XDuplicateRecordFields
51-- >>> import GHC.Generics
54-- >>> import GHC.Generics
52−-- >>> :m +Data.Generics.Internal.Lens
55+-- >>> :m +Data.Generics.Internal.VL.Lens
53-- >>> :{
56-- >>> :{
54-- data Human = Human
57-- data Human = Human
55-- { name :: String
58-- { name :: String
@@ -80,9 +83,9 @@ class Subtype sup sub where
80 --
83 --
81 -- >>> set (super @Animal) (Animal "dog" 10) human
84 -- >>> set (super @Animal) (Animal "dog" 10) human
82 -- Human {name = "dog", age = 10, address = "London"}
85 -- Human {name = "dog", age = 10, address = "London"}
83− super :: Lens' sub sup
86+ super :: VL.Lens sub sub sup sup
84− super f sub
87+ super
85− = fmap (`smash` sub) (f (upcast sub))
88+ = VL.lens upcast (uncurry smash . swap)
86
89
87 -- |Cast the more specific subtype to the more general supertype
90 -- |Cast the more specific subtype to the more general supertype
88 --
91 --
@@ -103,7 +106,7 @@ class Subtype sup sub where
103 -- >>> smash (Animal "dog" 10) human
106 -- >>> smash (Animal "dog" 10) human
104 -- Human {name = "dog", age = 10, address = "London"}
107 -- Human {name = "dog", age = 10, address = "London"}
105 smash :: sup -> sub -> sub
108 smash :: sup -> sub -> sub
106− smash = set (super @sup)
109+ smash = VL.set (super @sup)
107
110
108 {-# MINIMAL super | smash, upcast #-}
111 {-# MINIMAL super | smash, upcast #-}
109
112
@@ -112,14 +115,52 @@ instance
112 , Generic b
115 , Generic b
113 , GSmash (Rep a) (Rep b)
116 , GSmash (Rep a) (Rep b)
114 , GUpcast (Rep a) (Rep b)
117 , GUpcast (Rep a) (Rep b)
115− , ErrorUnless b a (CollectFieldsOrdered (Rep b) \\ CollectFieldsOrdered (Rep a))
118+ , CustomError a b
116 ) => Subtype b a where
119 ) => Subtype b a where
117 smash p b = to $ gsmash (from p) (from b)
120 smash p b = to $ gsmash (from p) (from b)
118 upcast = to . gupcast . from
121 upcast = to . gupcast . from
119
122
120−-- See Note [Uncluttering type signatures]
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
121instance {-# OVERLAPPING #-} Subtype a Void where
151instance {-# OVERLAPPING #-} Subtype a Void where
122 super = undefined
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
123instance {-# OVERLAPPING #-} Subtype Void a where
164instance {-# OVERLAPPING #-} Subtype Void a where
124 super = undefined
165 super = undefined
125
166
src/Data/Generics/Product/Typed.hs+39−11
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
@@ -7,13 +8,14 @@
7{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE TypeApplications #-}
9{-# LANGUAGE TypeApplications #-}
9{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeInType #-}
10{-# LANGUAGE TypeOperators #-}
12{-# LANGUAGE TypeOperators #-}
11{-# LANGUAGE UndecidableInstances #-}
13{-# LANGUAGE UndecidableInstances #-}
12
14
13-----------------------------------------------------------------------------
15-----------------------------------------------------------------------------
14-- |
16-- |
15-- Module : Data.Generics.Product.Typed
17-- Module : Data.Generics.Product.Typed
16−-- Copyright : (C) 2017 Csongor Kiss
18+-- Copyright : (C) 2019 Csongor Kiss
17-- License : BSD3
19-- License : BSD3
18-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
20-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
19-- Stability : experimental
21-- Stability : experimental
@@ -31,13 +33,15 @@ module Data.Generics.Product.Typed
31 ) where
33 ) where
32
34
33import Data.Generics.Internal.Families
35import Data.Generics.Internal.Families
34−import Data.Generics.Internal.Lens
36+import Data.Generics.Internal.VL.Lens as VL
35import Data.Generics.Internal.Void
37import Data.Generics.Internal.Void
36−import Data.Generics.Product.Internal.Typed
38+import Data.Generics.Product.Internal.GLens
37
39
38import Data.Kind (Constraint, Type)
40import Data.Kind (Constraint, Type)
39import GHC.Generics (Generic (Rep))
41import GHC.Generics (Generic (Rep))
40import GHC.TypeLits (TypeError, ErrorMessage (..))
42import GHC.TypeLits (TypeError, ErrorMessage (..))
43+import Data.Generics.Internal.Profunctor.Lens
44+import Data.Generics.Internal.Errors
41
45
42-- $setup
46-- $setup
43-- == /Running example:/
47-- == /Running example:/
@@ -46,7 +50,7 @@ import GHC.TypeLits (TypeError, ErrorMessage (..))
46-- >>> :set -XDataKinds
50-- >>> :set -XDataKinds
47-- >>> :set -XDeriveGeneric
51-- >>> :set -XDeriveGeneric
48-- >>> import GHC.Generics
52-- >>> import GHC.Generics
49−-- >>> :m +Data.Generics.Internal.Lens
53+-- >>> :m +Data.Generics.Internal.VL.Lens
50-- >>> :{
54-- >>> :{
51-- data Human
55-- data Human
52-- = Human
56-- = Human
@@ -91,9 +95,10 @@ class HasType a s where
91 -- ... The offending constructors are:
95 -- ... The offending constructors are:
92 -- ... HumanNoTall
96 -- ... HumanNoTall
93 -- ...
97 -- ...
94− typed :: Lens' s a
98+ typed :: VL.Lens s s a a
95− typed f t
99+ typed
96− = fmap (flip (setTyped @a) t) (f (getTyped @a t))
100+ = VL.lens (getTyped @a) (uncurry (setTyped @a) . swap)
101+ {-# INLINE typed #-}
97
102
98 -- |Get field at type.
103 -- |Get field at type.
99 getTyped :: s -> a
104 getTyped :: s -> a
@@ -101,19 +106,38 @@ class HasType a s where
101
106
102 -- |Set field at type.
107 -- |Set field at type.
103 setTyped :: a -> s -> s
108 setTyped :: a -> s -> s
104− setTyped = set (typed @a)
109+ setTyped = VL.set (typed @a)
105
110
106 {-# MINIMAL typed | setTyped, getTyped #-}
111 {-# MINIMAL typed | setTyped, getTyped #-}
107
112
108instance
113instance
109 ( Generic s
114 ( Generic s
110 , ErrorUnlessOne a s (CollectTotalType a (Rep s))
115 , ErrorUnlessOne a s (CollectTotalType a (Rep s))
111− , GHasType (Rep s) a
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
112 ) => HasType a s where
120 ) => HasType a s where
113
121
114− typed = ravel (repLens . gtyped)
122+ typed f s = VL.ravel (repLens . glens @(HasTotalTypePSym a)) f s
115
123
116−-- See Note [Uncluttering type signatures]
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.
117instance {-# OVERLAPPING #-} HasType a Void where
141instance {-# OVERLAPPING #-} HasType a Void where
118 typed = undefined
142 typed = undefined
119
143
@@ -148,3 +172,7 @@ type family ErrorUnlessOne (a :: Type) (s :: Type) (stat :: TypeStat) :: Constra
148
172
149 ErrorUnlessOne _ _ ('TypeStat '[] '[] _)
173 ErrorUnlessOne _ _ ('TypeStat '[] '[] _)
150 = ()
174 = ()
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.hs+333−17
@@ -1,9 +1,12 @@
1{-# LANGUAGE AllowAmbiguousTypes #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE DefaultSignatures #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE KindSignatures #-}
6{-# LANGUAGE KindSignatures #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE Rank2Types #-}
7{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE TypeApplications #-}
11{-# LANGUAGE TypeApplications #-}
9{-# LANGUAGE TypeFamilies #-}
12{-# LANGUAGE TypeFamilies #-}
@@ -13,7 +16,7 @@
13-----------------------------------------------------------------------------
16-----------------------------------------------------------------------------
14-- |
17-- |
15-- Module : Data.Generics.Product.Types
18-- Module : Data.Generics.Product.Types
16−-- Copyright : (C) 2017 Csongor Kiss
19+-- Copyright : (C) 2019 Csongor Kiss
17-- License : BSD3
20-- License : BSD3
18-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
21-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
19-- Stability : experimental
22-- Stability : experimental
@@ -26,26 +29,339 @@
26module Data.Generics.Product.Types
29module Data.Generics.Product.Types
27 ( -- *Traversals
30 ( -- *Traversals
28 --
31 --
29− -- $example
32+ -- $setup
30− HasTypes (..)
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+
31 ) where
46 ) where
32
47
33−--import Data.Generics.Internal.Families
48+import Data.Kind
34−import Data.Generics.Internal.Lens
49+import Data.Int (Int8, Int16, Int32, Int64)
35−import Data.Generics.Product.Internal.Types
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+--------------------------------------------------------------------------------
36
78
37−--import Data.Kind (Constraint, Type)
79+-- TODO [1.0.0.0]: use type-changing variant internally
38−import GHC.Generics (Generic (Rep), from, to)
39−import Boggle
40−--import GHC.TypeLits (TypeError, ErrorMessage (..))
41
80
42−class HasTypes a s where
81+-- | Traverse all types in the given structure.
43− types :: Traversal' s a
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_ #-}
44
101
45instance
102instance
46− ( Generic s
103+ ( HasTypesUsing ChGeneric s a
47− , GHasTypes (Rep s) a
104+ ) => HasTypes s a where
48− ) => HasTypes a s 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
49
363
50− types f s = lowerBoggle (to <$> gtypes f (from s))
364+type family IsNothing a where
365+ IsNothing ('Just _) = 'False
366+ IsNothing 'Nothing = 'True
51
367
src/Data/Generics/Sum.hs+1−1
@@ -1,7 +1,7 @@
1-----------------------------------------------------------------------------
1-----------------------------------------------------------------------------
2-- |
2-- |
3-- Module : Data.Generics.Sum
3-- Module : Data.Generics.Sum
4−-- Copyright : (C) 2017 Csongor Kiss
4+-- Copyright : (C) 2019 Csongor Kiss
5-- License : BSD3
5-- License : BSD3
6-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
6-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
7-- Stability : experimental
7-- Stability : experimental
src/Data/Generics/Sum/Any.hs+4−4
@@ -11,7 +11,7 @@
11-----------------------------------------------------------------------------
11-----------------------------------------------------------------------------
12-- |
12-- |
13-- Module : Data.Generics.Sum.Any
13-- Module : Data.Generics.Sum.Any
14−-- Copyright : (C) 2017 Csongor Kiss
14+-- Copyright : (C) 2019 Csongor Kiss
15-- License : BSD3
15-- License : BSD3
16-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
16-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17-- Stability : experimental
17-- Stability : experimental
@@ -28,9 +28,9 @@ module Data.Generics.Sum.Any
28 AsAny (..)
28 AsAny (..)
29 ) where
29 ) where
30
30
31−import Data.Generics.Internal.Lens
32import Data.Generics.Sum.Constructors
31import Data.Generics.Sum.Constructors
33import Data.Generics.Sum.Typed
32import Data.Generics.Sum.Typed
33+import Data.Generics.Internal.VL.Prism
34
34
35-- $setup
35-- $setup
36-- == /Running example:/
36-- == /Running example:/
@@ -39,7 +39,7 @@ import Data.Generics.Sum.Typed
39-- >>> :set -XDataKinds
39-- >>> :set -XDataKinds
40-- >>> :set -XDeriveGeneric
40-- >>> :set -XDeriveGeneric
41-- >>> import GHC.Generics
41-- >>> import GHC.Generics
42−-- >>> :m +Data.Generics.Internal.Lens
42+-- >>> :m +Data.Generics.Internal.VL.Prism
43-- >>> :{
43-- >>> :{
44-- data Animal
44-- data Animal
45-- = Dog Dog
45-- = Dog Dog
@@ -61,7 +61,7 @@ import Data.Generics.Sum.Typed
61-- :}
61-- :}
62
62
63-- |Sums that have generic prisms.
63-- |Sums that have generic prisms.
64−class AsAny (sel :: k) a s | s sel k -> a where
64+class AsAny sel a s | s sel -> a where
65 -- |A prism that projects a sum as identified by some selector. Currently
65 -- |A prism that projects a sum as identified by some selector. Currently
66 -- supported selectors are constructor names and unique types. Compatible
66 -- supported selectors are constructor names and unique types. Compatible
67 -- with the lens package's 'Control.Lens.Prism' type.
67 -- with the lens package's 'Control.Lens.Prism' type.
src/Data/Generics/Sum/Constructors.hs+93−27
@@ -1,4 +1,5 @@
1{-# LANGUAGE AllowAmbiguousTypes #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
@@ -14,7 +15,7 @@
14-----------------------------------------------------------------------------
15-----------------------------------------------------------------------------
15-- |
16-- |
16-- Module : Data.Generics.Sum.Constructors
17-- Module : Data.Generics.Sum.Constructors
17−-- Copyright : (C) 2017 Csongor Kiss
18+-- Copyright : (C) 2019 Csongor Kiss
18-- License : BSD3
19-- License : BSD3
19-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
20-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
20-- Stability : experimental
21-- Stability : experimental
@@ -29,18 +30,22 @@ module Data.Generics.Sum.Constructors
29
30
30 -- $setup
31 -- $setup
31 AsConstructor (..)
32 AsConstructor (..)
32− , _CtorRaw
33+ , AsConstructor_ (..)
34+ , AsConstructor' (..)
35+ , AsConstructor0 (..)
33 ) where
36 ) where
34
37
35import Data.Generics.Internal.Families
38import Data.Generics.Internal.Families
36−import Data.Generics.Internal.Lens
37import Data.Generics.Internal.Void
39import Data.Generics.Internal.Void
38import Data.Generics.Sum.Internal.Constructors
40import Data.Generics.Sum.Internal.Constructors
39
41
40import Data.Kind (Constraint, Type)
42import Data.Kind (Constraint, Type)
41import GHC.Generics (Generic (Rep))
43import GHC.Generics (Generic (Rep))
42import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
44import GHC.TypeLits (Symbol, TypeError, ErrorMessage (..))
43−import Data.Profunctor.Extra
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
44
49
45-- $setup
50-- $setup
46-- == /Running example:/
51-- == /Running example:/
@@ -51,7 +56,7 @@ import Data.Profunctor.Extra
51-- >>> :set -XFlexibleContexts
56-- >>> :set -XFlexibleContexts
52-- >>> :set -XTypeFamilies
57-- >>> :set -XTypeFamilies
53-- >>> import GHC.Generics
58-- >>> import GHC.Generics
54−-- >>> :m +Data.Generics.Internal.Lens
59+-- >>> :m +Data.Generics.Internal.VL.Prism
55-- >>> :m +Data.Generics.Product.Fields
60-- >>> :m +Data.Generics.Product.Fields
56-- >>> :m +Data.Function
61-- >>> :m +Data.Function
57-- >>> :{
62-- >>> :{
@@ -66,7 +71,7 @@ import Data.Profunctor.Extra
66-- , age :: Age
71-- , age :: Age
67-- , fieldA :: a
72-- , fieldA :: a
68-- }
73-- }
69−-- deriving (Generic, Show)
74+-- deriving Show
70-- type Name = String
75-- type Name = String
71-- type Age = Int
76-- type Age = Int
72-- dog, cat, duck :: Animal Int
77-- dog, cat, duck :: Animal Int
@@ -101,38 +106,99 @@ class AsConstructor (ctor :: Symbol) s t a b | ctor s -> a, ctor t -> b where
101 -- ...
106 -- ...
102 _Ctor :: Prism s t a b
107 _Ctor :: Prism s t a b
103
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
127+
104instance
128instance
105 ( Generic s
129 ( Generic s
106 , ErrorUnless ctor s (HasCtorP ctor (Rep s))
130 , ErrorUnless ctor s (HasCtorP ctor (Rep s))
107− , Generic t
108− , s' ~ Proxied s
109− , Generic s'
110 , GAsConstructor' ctor (Rep s) a
131 , GAsConstructor' ctor (Rep s) a
111− , 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'
112 , GAsConstructor ctor (Rep s) (Rep t) a b
145 , GAsConstructor ctor (Rep s) (Rep t) a b
113− , '(t, b) ~ Infer s' a' a (PickTv 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
114 ) => AsConstructor ctor s t a b where
150 ) => AsConstructor ctor s t a b where
115
151
116− _Ctor eta = prismRavel (repIsoE . _GCtor @ctor) eta
152+ _Ctor = _Ctor0 @ctor
117− {-# INLINE _Ctor #-}
153+ {-# INLINE[2] _Ctor #-}
118−
119−-- Used for testing the optimisation passes
120−_CtorRaw :: forall ctor s t s' a' a b . ( Generic s
121− , ErrorUnless ctor s (HasCtorP ctor (Rep s))
122− , Generic t
123− , s' ~ Proxied s
124− , Generic s'
125− , GAsConstructor' ctor (Rep s) a
126− , GAsConstructor' ctor (Rep s') a'
127− , GAsConstructor ctor (Rep s) (Rep t) a b
128− , '(t, b) ~ Infer s' a' a (PickTv a' b)
129− ) => PrismE s t a b
130−_CtorRaw = repIsoE . _GCtor @ctor
131
154
132−-- See Note [Uncluttering type signatures]
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
133instance {-# OVERLAPPING #-} AsConstructor ctor (Void1 a) (Void1 b) a b where
169instance {-# OVERLAPPING #-} AsConstructor ctor (Void1 a) (Void1 b) a b where
134 _Ctor = undefined
170 _Ctor = undefined
135
171
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 #-}
201+
136type family ErrorUnless (ctor :: Symbol) (s :: Type) (contains :: Bool) :: Constraint where
202type family ErrorUnless (ctor :: Symbol) (s :: Type) (contains :: Bool) :: Constraint where
137 ErrorUnless ctor s 'False
203 ErrorUnless ctor s 'False
138 = TypeError
204 = TypeError
src/Data/Generics/Sum/Internal/Constructors.hs+19−16
@@ -14,7 +14,7 @@
14-----------------------------------------------------------------------------
14-----------------------------------------------------------------------------
15-- |
15-- |
16-- Module : Data.Generics.Sum.Internal.Constructors
16-- Module : Data.Generics.Sum.Internal.Constructors
17−-- Copyright : (C) 2017 Csongor Kiss
17+-- Copyright : (C) 2019 Csongor Kiss
18-- License : BSD3
18-- License : BSD3
19-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
19-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
20-- Stability : experimental
20-- Stability : experimental
@@ -30,44 +30,47 @@ module Data.Generics.Sum.Internal.Constructors
30 ) where
30 ) where
31
31
32import Data.Generics.Internal.Families
32import Data.Generics.Internal.Families
33−import Data.Generics.Internal.HList
33+import Data.Generics.Product.Internal.HList
34−import Data.Generics.Internal.Lens
34+import Data.Profunctor (Profunctor(..))
35
35
36import GHC.Generics
36import GHC.Generics
37import GHC.TypeLits (Symbol)
37import GHC.TypeLits (Symbol)
38+import Data.Generics.Internal.Profunctor.Lens
39+import Data.Generics.Internal.Profunctor.Iso
40+import Data.Generics.Internal.Profunctor.Prism
38
41
39-- |As 'AsConstructor' but over generic representations as defined by
42-- |As 'AsConstructor' but over generic representations as defined by
40-- "GHC.Generics".
43-- "GHC.Generics".
41class GAsConstructor (ctor :: Symbol) s t a b | ctor s -> a, ctor t -> b where
44class GAsConstructor (ctor :: Symbol) s t a b | ctor s -> a, ctor t -> b where
42− _GCtor :: PrismE (s x) (t x) a b
45+ _GCtor :: Prism (s x) (t x) a b
43
46
44type GAsConstructor' ctor s a = GAsConstructor ctor s s a a
47type GAsConstructor' ctor s a = GAsConstructor ctor s s a a
45
48
46instance
49instance
47− ( GCollectible f as
50+ ( GIsList f f as as
48− , GCollectible g bs
51+ , GIsList g g bs bs
49 , ListTuple a as
52 , ListTuple a as
50 , ListTuple b bs
53 , ListTuple b bs
51 ) => GAsConstructor ctor (M1 C ('MetaCons ctor fixity fields) f) (M1 C ('MetaCons ctor fixity fields) g) a b where
54 ) => GAsConstructor ctor (M1 C ('MetaCons ctor fixity fields) f) (M1 C ('MetaCons ctor fixity fields) g) a b where
52
55
53− _GCtor = prismE (M1 . gfromCollection . tupleToList) (Right . listToTuple . gtoCollection . unM1)
56+ _GCtor = dimap (listToTuple . view glist . unM1) (M1 . view (fromIso glist) . tupleToList)
54− {-# INLINE _GCtor #-}
57+ {-# INLINE[0] _GCtor #-}
55
58
56instance GSumAsConstructor ctor (HasCtorP ctor l) l r l' r' a b => GAsConstructor ctor (l :+: r) (l' :+: r') a b where
59instance GSumAsConstructor ctor (HasCtorP ctor l) l r l' r' a b => GAsConstructor ctor (l :+: r) (l' :+: r') a b where
57 _GCtor = _GSumCtor @ctor @(HasCtorP ctor l)
60 _GCtor = _GSumCtor @ctor @(HasCtorP ctor l)
58− {-# INLINE _GCtor #-}
61+ {-# INLINE[0] _GCtor #-}
59
62
60instance GAsConstructor ctor f f' a b => GAsConstructor ctor (M1 D meta f) (M1 D meta f') a b where
63instance GAsConstructor ctor f f' a b => GAsConstructor ctor (M1 D meta f) (M1 D meta f') a b where
61− _GCtor = mIsoE . _GCtor @ctor
64+ _GCtor = mIso . _GCtor @ctor
62− {-# INLINE _GCtor #-}
65+ {-# INLINE[0] _GCtor #-}
63
66
64class GSumAsConstructor (ctor :: Symbol) (contains :: Bool) l r l' r' a b | ctor l r -> a, ctor l' r' -> b where
67class GSumAsConstructor (ctor :: Symbol) (contains :: Bool) l r l' r' a b | ctor l r -> a, ctor l' r' -> b where
65− _GSumCtor :: PrismE ((l :+: r) x) ((l' :+: r') x) a b
68+ _GSumCtor :: Prism ((l :+: r) x) ((l' :+: r') x) a b
66
69
67instance GAsConstructor ctor l l' a b => GSumAsConstructor ctor 'True l r l' r a b where
70instance GAsConstructor ctor l l' a b => GSumAsConstructor ctor 'True l r l' r a b where
68− _GSumCtor = leftE . _GCtor @ctor
71+ _GSumCtor = left . _GCtor @ctor
69− {-# INLINE _GSumCtor #-}
72+ {-# INLINE[0] _GSumCtor #-}
70
73
71instance GAsConstructor ctor r r' a b => GSumAsConstructor ctor 'False l r l r' a b where
74instance GAsConstructor ctor r r' a b => GSumAsConstructor ctor 'False l r l r' a b where
72− _GSumCtor = rightE . _GCtor @ctor
75+ _GSumCtor = right . _GCtor @ctor
73− {-# INLINE _GSumCtor #-}
76+ {-# INLINE[0] _GSumCtor #-}
src/Data/Generics/Sum/Internal/Subtype.hs+77−36
@@ -1,7 +1,10 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE DataKinds #-}
1{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE KindSignatures #-}
5{-# LANGUAGE KindSignatures #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PolyKinds #-}
5{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE TypeApplications #-}
9{-# LANGUAGE TypeApplications #-}
7{-# LANGUAGE TypeOperators #-}
10{-# LANGUAGE TypeOperators #-}
@@ -11,7 +14,7 @@
11-----------------------------------------------------------------------------
14-----------------------------------------------------------------------------
12-- |
15-- |
13-- Module : Data.Generics.Sum.Internal.Subtype
16-- Module : Data.Generics.Sum.Internal.Subtype
14−-- Copyright : (C) 2017 Csongor Kiss
17+-- Copyright : (C) 2019 Csongor Kiss
15-- License : BSD3
18-- License : BSD3
16-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
19-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17-- Stability : experimental
20-- Stability : experimental
@@ -25,50 +28,88 @@ module Data.Generics.Sum.Internal.Subtype
25 ( GAsSubtype (..)
28 ( GAsSubtype (..)
26 ) where
29 ) where
27
30
28−import Data.Generics.Internal.HList
31+import Data.Generics.Product.Internal.HList
29import Data.Generics.Sum.Internal.Typed
32import Data.Generics.Sum.Internal.Typed
30
33
31import Data.Kind
34import Data.Kind
32import GHC.Generics
35import GHC.Generics
36+import Data.Generics.Internal.Profunctor.Iso
37+import Data.Generics.Internal.Profunctor.Prism
38+import Data.Generics.Internal.Families.Has
33
39
34-- |As 'AsSubtype' but over generic representations as defined by
40-- |As 'AsSubtype' but over generic representations as defined by
35-- "GHC.Generics".
41-- "GHC.Generics".
36class GAsSubtype (subf :: Type -> Type) (supf :: Type -> Type) where
42class GAsSubtype (subf :: Type -> Type) (supf :: Type -> Type) where
37− ginjectSub :: subf x -> supf x
43+ _GSub :: Prism' (supf x) (subf x)
38− gprojectSub :: supf x -> Either (supf x) (subf x)
39
44
40instance
45instance
41− ( GAsSubtype l supf
46+ ( GSplash sub sup
42− , GAsSubtype r supf
47+ , GDowncast sub sup
43− ) => GAsSubtype (l :+: r) supf where
48+ ) => GAsSubtype sub sup where
44−
49+ _GSub f = prism _GSplash _GDowncast f
45− ginjectSub x = case x of
50+ {-# INLINE[0] _GSub #-}
46− L1 l -> ginjectSub l
51+
47− R1 r -> ginjectSub r
52+--------------------------------------------------------------------------------
48− gprojectSub x
53+
49− = case gprojectSub x of
54+class GSplash (sub :: Type -> Type) (sup :: Type -> Type) where
50− Left _ -> fmap R1 (gprojectSub x)
55+ _GSplash :: sub x -> sup x
51− Right y -> Right (L1 y)
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)
52
77
53instance
78instance
54− ( GAsType supf a
79+ ( GIsList sup sup as as
55− , GCollectible subf as
80+ , GDowncastC (HasPartialTypeP as sub) sub sup
56− , ListTuple a as
81+ ) => GDowncast sub (C1 m sup) where
57− ) => GAsSubtype (C1 meta subf) supf where
82+ _GDowncast (M1 m) = case _GDowncastC @(HasPartialTypeP as sub) m of
58−
83+ Left _ -> Left (M1 m)
59− ginjectSub
84+ Right r -> Right r
60− = ginjectTyped . listToTuple . gtoCollection . unM1
85+ {-# INLINE[0] _GDowncast #-}
61− gprojectSub
86+
62− = fmap (M1 . gfromCollection . tupleToList) . gprojectTyped
87+instance (GDowncast sub l, GDowncast sub r) => GDowncast sub (l :+: r) where
63−
88+ _GDowncast (L1 x) = case _GDowncast x of
64−instance GAsType supf a => GAsSubtype (S1 meta (Rec0 a)) supf where
89+ Left _ -> Left (L1 x)
65− ginjectSub
90+ Right r -> Right r
66− = ginjectTyped @supf . unK1 . unM1
91+ _GDowncast (R1 x) = case _GDowncast x of
67− gprojectSub
92+ Left _ -> Left (R1 x)
68− = fmap (M1 . K1) . gprojectTyped @supf
93+ Right r -> Right r
69−
94+ {-# INLINE[0] _GDowncast #-}
70−instance GAsSubtype subf supf => GAsSubtype (D1 meta subf) supf where
95+
71− ginjectSub
96+instance GDowncast sub sup => GDowncast sub (D1 m sup) where
72− = ginjectSub . unM1
97+ _GDowncast (M1 m) = case _GDowncast m of
73− gprojectSub
98+ Left _ -> Left (M1 m)
74− = fmap M1 . gprojectSub
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 #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE KindSignatures #-}
5+{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
8+{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
9+{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
10+{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
11+{-# LANGUAGE UndecidableInstances #-}
12
12
13-----------------------------------------------------------------------------
13-----------------------------------------------------------------------------
14-- |
14-- |
15-- Module : Data.Generics.Sum.Internal.Typed
15-- Module : Data.Generics.Sum.Internal.Typed
16−-- Copyright : (C) 2017 Csongor Kiss
16+-- Copyright : (C) 2019 Csongor Kiss
17-- License : BSD3
17-- License : BSD3
18-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
18-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
19-- Stability : experimental
19-- Stability : experimental
@@ -29,60 +29,44 @@ module Data.Generics.Sum.Internal.Typed
29
29
30import Data.Kind
30import Data.Kind
31import GHC.Generics
31import GHC.Generics
32+import Data.Tagged
32
33
33import Data.Generics.Internal.Families
34import Data.Generics.Internal.Families
34−import Data.Generics.Internal.HList
35+import Data.Generics.Product.Internal.HList
35−import Data.Generics.Internal.Lens
36+import Data.Generics.Internal.Profunctor.Iso
37+import Data.Generics.Internal.Profunctor.Prism
36
38
37-- |As 'AsType' but over generic representations as defined by "GHC.Generics".
39-- |As 'AsType' but over generic representations as defined by "GHC.Generics".
38−class GAsType (f :: Type -> Type) a where
40+class GAsType (f :: Type -> Type) (as :: [Type]) where
39− _GTyped :: Prism' (f x) a
41+ _GTyped :: Prism (f x) (f x) (HList as) (HList as)
40− _GTyped = prism ginjectTyped gprojectTyped
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) #-}
41
47
42− ginjectTyped :: a -> f x
43− gprojectTyped :: f x -> Either (f x) a
44
48
45instance
49instance
46− ( GCollectible f as
50+ ( GIsList f f as as
47− , ListTuple a as
51+ ) => GAsType (M1 C meta f) as where
48− ) => GAsType (M1 C meta f) a where
52+ _GTyped = mIso . glist
53+ {-# INLINE[0] _GTyped #-}
49
54
50− ginjectTyped
55+instance GSumAsType (HasPartialTypeP a l) l r a => GAsType (l :+: r) a where
51− = M1 . gfromCollection . tupleToList
56+ _GTyped = _GSumTyped @(HasPartialTypeP a l)
52− gprojectTyped
57+ {-# INLINE[0] _GTyped #-}
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
60
58
61instance GAsType f a => GAsType (M1 D meta f) a where
59instance GAsType f a => GAsType (M1 D meta f) a where
62− ginjectTyped
60+ _GTyped = mIso . _GTyped
63− = M1 . ginjectTyped
61+ {-# INLINE[0] _GTyped #-}
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)
70
62
71− ginjectSumTyped :: a -> (l :+: r) x
63+class GSumAsType (contains :: Bool) l r (a :: [Type]) where
72− gprojectSumTyped :: (l :+: r) x -> Either ((l :+: r) x) a
64+ _GSumTyped :: Prism ((l :+: r) x) ((l :+: r) x) (HList a) (HList a)
73
65
74instance GAsType l a => GSumAsType 'True l r a where
66instance GAsType l a => GSumAsType 'True l r a where
75− ginjectSumTyped
67+ _GSumTyped = left . _GTyped
76− = L1 . ginjectTyped
68+ {-# INLINE[0] _GSumTyped #-}
77− gprojectSumTyped x
78− = case x of
79− L1 l -> either (Left . L1) Right (gprojectTyped l)
80− R1 _ -> Left x
81
69
82instance GAsType r a => GSumAsType 'False l r a where
70instance GAsType r a => GSumAsType 'False l r a where
83− ginjectSumTyped
71+ _GSumTyped = right . _GTyped
84− = R1 . ginjectTyped
72+ {-# INLINE[0] _GSumTyped #-}
85− gprojectSumTyped x
86− = case x of
87− R1 r -> either (Left . R1) Right (gprojectTyped r)
88− L1 _ -> Left x
src/Data/Generics/Sum/Subtype.hs+54−10
@@ -1,7 +1,10 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE MonoLocalBinds #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE TypeApplications #-}
5{-# LANGUAGE TypeOperators #-}
8{-# LANGUAGE TypeOperators #-}
6{-# LANGUAGE TypeSynonymInstances #-}
9{-# LANGUAGE TypeSynonymInstances #-}
7{-# LANGUAGE UndecidableInstances #-}
10{-# LANGUAGE UndecidableInstances #-}
@@ -9,7 +12,7 @@
9-----------------------------------------------------------------------------
12-----------------------------------------------------------------------------
10-- |
13-- |
11-- Module : Data.Generics.Sum.Subtype
14-- Module : Data.Generics.Sum.Subtype
12−-- Copyright : (C) 2017 Csongor Kiss
15+-- Copyright : (C) 2019 Csongor Kiss
13-- License : BSD3
16-- License : BSD3
14-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
17-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
15-- Stability : experimental
18-- Stability : experimental
@@ -26,11 +29,12 @@ module Data.Generics.Sum.Subtype
26 AsSubtype (..)
29 AsSubtype (..)
27 ) where
30 ) where
28
31
29−import Data.Generics.Internal.Lens
30import Data.Generics.Internal.Void
32import Data.Generics.Internal.Void
31import Data.Generics.Sum.Internal.Subtype
33import Data.Generics.Sum.Internal.Subtype
32
34
33−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
34
38
35-- $setup
39-- $setup
36-- == /Running example:/
40-- == /Running example:/
@@ -39,7 +43,7 @@ import GHC.Generics (Generic (Rep, to, from))
39-- >>> :set -XDataKinds
43-- >>> :set -XDataKinds
40-- >>> :set -XDeriveGeneric
44-- >>> :set -XDeriveGeneric
41-- >>> import GHC.Generics
45-- >>> import GHC.Generics
42−-- >>> :m +Data.Generics.Internal.Lens
46+-- >>> :m +Data.Generics.Internal.VL.Prism
43-- >>> :{
47-- >>> :{
44-- data Animal
48-- data Animal
45-- = Dog Dog
49-- = Dog Dog
@@ -86,15 +90,20 @@ class AsSubtype sub sup where
86 -- >>> duck ^? _Sub :: Maybe FourLeggedAnimal
90 -- >>> duck ^? _Sub :: Maybe FourLeggedAnimal
87 -- Nothing
91 -- Nothing
88 _Sub :: Prism' sup sub
92 _Sub :: Prism' sup sub
89− _Sub = prism injectSub projectSub
93+ _Sub = prism injectSub (\i -> maybe (Left i) Right (projectSub i))
94+ {-# INLINE[2] _Sub #-}
90
95
91 -- |Injects a subtype into a supertype (upcast).
96 -- |Injects a subtype into a supertype (upcast).
92 injectSub :: sub -> sup
97 injectSub :: sub -> sup
98+ injectSub
99+ = build (_Sub @sub @sup)
93
100
94 -- |Projects a subtype from a supertype (downcast).
101 -- |Projects a subtype from a supertype (downcast).
95− projectSub :: sup -> Either sup sub
102+ projectSub :: sup -> Maybe sub
103+ projectSub
104+ = either (const Nothing) Just . match (_Sub @sub @sup)
96
105
97− {-# MINIMAL injectSub, projectSub #-}
106+ {-# MINIMAL (injectSub, projectSub) | _Sub #-}
98
107
99instance
108instance
100 ( Generic sub
109 ( Generic sub
@@ -102,13 +111,48 @@ instance
102 , GAsSubtype (Rep sub) (Rep sup)
111 , GAsSubtype (Rep sub) (Rep sup)
103 ) => AsSubtype sub sup where
112 ) => AsSubtype sub sup where
104
113
105− injectSub = to . ginjectSub . from
114+ _Sub f = prismRavel (repIso . _GSub . fromIso repIso) f
106− projectSub = either (Left . to) (Right . to) . gprojectSub . from
115+ {-# INLINE[2] _Sub #-}
107
116
108−-- See Note [Uncluttering type signatures]
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
109instance {-# OVERLAPPING #-} AsSubtype a Void where
138instance {-# OVERLAPPING #-} AsSubtype a Void where
110 injectSub = undefined
139 injectSub = undefined
111 projectSub = 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
112instance {-# OVERLAPPING #-} AsSubtype Void a where
156instance {-# OVERLAPPING #-} AsSubtype Void a where
113 injectSub = undefined
157 injectSub = undefined
114 projectSub = undefined
158 projectSub = undefined
src/Data/Generics/Sum/Typed.hs+53−12
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeApplications #-}
1{-# LANGUAGE AllowAmbiguousTypes #-}
3{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
@@ -12,7 +14,7 @@
12-----------------------------------------------------------------------------
14-----------------------------------------------------------------------------
13-- |
15-- |
14-- Module : Data.Generics.Sum.Typed
16-- Module : Data.Generics.Sum.Typed
15−-- Copyright : (C) 2017 Csongor Kiss
17+-- Copyright : (C) 2019 Csongor Kiss
16-- License : BSD3
18-- License : BSD3
17-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
19-- Maintainer : Csongor Kiss <kiss.csongor.kiss@gmail.com>
18-- Stability : experimental
20-- Stability : experimental
@@ -33,17 +35,21 @@ import Data.Kind
33import GHC.Generics
35import GHC.Generics
34import GHC.TypeLits (TypeError, ErrorMessage (..), Symbol)
36import GHC.TypeLits (TypeError, ErrorMessage (..), Symbol)
35import Data.Generics.Sum.Internal.Typed
37import Data.Generics.Sum.Internal.Typed
38+import Data.Generics.Internal.Errors
36
39
37import Data.Generics.Internal.Families
40import Data.Generics.Internal.Families
38−import Data.Generics.Internal.Lens
39import Data.Generics.Internal.Void
41import 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)
40
46
41-- $setup
47-- $setup
42-- >>> :set -XTypeApplications
48-- >>> :set -XTypeApplications
43-- >>> :set -XDataKinds
49-- >>> :set -XDataKinds
44-- >>> :set -XDeriveGeneric
50-- >>> :set -XDeriveGeneric
45-- >>> import GHC.Generics
51-- >>> import GHC.Generics
46−-- >>> :m +Data.Generics.Internal.Lens
52+-- >>> :m +Data.Generics.Internal.VL.Prism
47-- >>> :{
53-- >>> :{
48-- data Animal
54-- data Animal
49-- = Dog Dog
55-- = Dog Dog
@@ -84,31 +90,66 @@ class AsType a s where
84 -- ... Turtle
90 -- ... Turtle
85 -- ...
91 -- ...
86 _Typed :: Prism' s a
92 _Typed :: Prism' s a
93+ _Typed = prism injectTyped (\i -> maybe (Left i) Right (projectTyped i))
94+ {-# INLINE[2] _Typed #-}
87
95
88 -- |Inject by type.
96 -- |Inject by type.
89 injectTyped :: a -> s
97 injectTyped :: a -> s
98+ injectTyped
99+ = build _Typed
90
100
91 -- |Project by type.
101 -- |Project by type.
92 projectTyped :: s -> Maybe a
102 projectTyped :: s -> Maybe a
103+ projectTyped
104+ = either (const Nothing) Just . match _Typed
105+
106+ {-# MINIMAL (injectTyped, projectTyped) | _Typed #-}
93
107
94instance
108instance
95 ( Generic s
109 ( Generic s
96− , ErrorUnlessOne a s (CollectPartialType a (Rep s))
110+ , ErrorUnlessOne a s (CollectPartialType as (Rep s))
97− , GAsType (Rep s) a
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)
98 ) => AsType a s where
117 ) => AsType a s where
99
118
100− _Typed
119+ _Typed eta = prismRavel (prismPRavel (repIso . _GTyped @_ @as . tupled)) eta
101− = repIso . _GTyped
120+ {-# INLINE[2] _Typed #-}
102− injectTyped
103− = to . ginjectTyped
104− projectTyped
105− = either (const Nothing) Just . gprojectTyped . from
106
121
107−-- See Note [Uncluttering type signatures]
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
108instance {-# OVERLAPPING #-} AsType a Void where
134instance {-# OVERLAPPING #-} AsType a Void where
109 _Typed = undefined
135 _Typed = undefined
110 injectTyped = undefined
136 injectTyped = undefined
111 projectTyped = 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
112instance {-# OVERLAPPING #-} AsType Void a where
153instance {-# OVERLAPPING #-} AsType Void a where
113 _Typed = undefined
154 _Typed = undefined
114 injectTyped = undefined
155 injectTyped = undefined
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 #-}
src/Data/Profunctor/Extra.hsdeleted−645
@@ -1,645 +0,0 @@
1−{-# LANGUAGE GADTs #-}
2−{-# LANGUAGE TypeApplications #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−{-# LANGUAGE RankNTypes #-}
5−{-# LANGUAGE DeriveGeneric #-}
6−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# OPTIONS_GHC -funfolding-creation-threshold=100000 #-}
8−module Data.Profunctor.Extra where
9−
10−import Data.Profunctor
11−import GHC.Generics
12−import Debug.Trace
13−import Boggle hiding ((<.>))
14−
15−
16−-- Pull dimaps to left and rights to right
17−{-
18−data Boggle p a b where
19− DRight :: (p x y) -> Boggle p (Either z x) (Either z y)
20− DMap :: (c -> a) -> (b -> d) -> (p a b) -> Boggle p c d
21− Dirty :: p a b -> Boggle p a b
22−
23−instance Choice p => Profunctor (Boggle p) where
24− dimap f g (Dirty pab) = DMap f g pab
25− dimap f g (DMap f' g' pab) = DMap (f' . f) (g . g') pab
26− dimap f g (DRight pab) = DMap f g (right' pab)
27−
28−instance Choice p => Choice (Boggle p) where
29− right' (Dirty pab) = DRight pab
30− right' (DMap f g pab) = DMap (fmap f) (fmap g) (right' pab)
31− right' (DRight pab) = DRight (right' pab)
32−
33−lift :: p a b -> Boggle p a b
34−lift pab = Dirty pab
35−
36−
37−lower :: Choice p => Boggle p a b -> p a b
38−lower (Dirty pab) = pab
39−lower (DMap f g pab) = dimap f g pab
40−lower (DRight pab) = right' pab
41−
42−test1 :: Choice p => p Int Int -> p (Either z Int) (Either z Int)
43−test1 pab = right' (dimap (+1) (subtract 1) pab)
44−
45−test1spec :: Choice p => p Int Int -> p (Either z Int) (Either z Int)
46−test1spec = lower . test1 . lift
47−-}
48−
49−
50−------------------------------------------------------------------------------
51−
52−--- This type fuses together uses of `dimap` we want to do this last
53−-- as we first need to rearrange the "Eithers"
54−newtype DimapK p b c = DimapK (forall a d . (Fun a b -> Fun c d -> p a d))
55−
56−liftDimapK :: MProfunctor p => p a b -> DimapK p a b
57−liftDimapK pa = DimapK (\f g -> mdimap f g pa)
58−
59−lowerDimapK :: DimapK p a b -> p a b
60−lowerDimapK (DimapK pa) = pa (liftFun id) (liftFun id)
61−
62−-- Note that here because we use (<.>) we potentially flatten "either"
63−instance MProfunctor (DimapK p) where
64− mdimap f g (DimapK x) = DimapK (\y z -> x (f <.> y) (z <.> g))
65− {-# INLINE mdimap #-}
66−
67−fmdimap :: Fun a b -> Fun c d -> DimapK p b c -> p a d
68−fmdimap f g (DimapK x) = x f g
69−
70−------------------------------------------------------------------------------
71−-- Wrapper to ensure that a liftDimapK followed by lowerDimapK doesn't
72−-- introduce a dimap.
73−
74−data DimapK1 p b c = DimapK1 (p b c) (DimapK p b c)
75− | DirtyDimapK1 (p b c)
76−
77−liftDimapK1 :: MProfunctor p => p a b -> DimapK1 p a b
78−liftDimapK1 pab = DimapK1 pab (liftDimapK pab)
79−
80−lowerDimapK1 :: DimapK1 p a b -> p a b
81−lowerDimapK1 (DimapK1 pab _) = pab
82−lowerDimapK1 (DirtyDimapK1 pab) = pab
83−
84−instance MProfunctor p => MProfunctor (DimapK1 p) where
85− mdimap f g (DimapK1 _ d) = DimapK1 (fmdimap f g d) (mdimap f g d)
86− mdimap f g (DirtyDimapK1 pab) = DimapK1 (mdimap f g pab) (mdimap f g (liftDimapK pab))
87− {-# INLINE mdimap #-}
88−
89−--instance MRight p => MRight (DiMapK1 p) where
90−-- mright (DimapK1
91−instance MRight p => MRight (DimapK1 p) where
92− mright (DimapK1 f g) = DirtyDimapK1 (mright f)
93− mright (DirtyDimapK1 pab) = DirtyDimapK1 (mright pab)
94− {-# INLINE mright #-}
95−
96−-- Should be the identity, works
97−testDimapK1 :: (MProfunctor p, MRight p) => p Int Int -> p Int Int
98−testDimapK1 pab = lowerDimapK1 (liftDimapK1 pab)
99−
100−a, b :: Fun Int Int
101−a = liftFun id
102−b = liftFun id
103−
104−-- Should observe one dimap in the specialised version
105−testDimapK1A :: (MProfunctor p) => p Int Int -> p Int Int
106−testDimapK1A pab = mdimap a b (mdimap a b pab)
107−
108−ravelDimapK1 :: (MProfunctor p)
109− => (DimapK1 p a b -> DimapK1 p c d)
110− -> p a b -> p c d
111−ravelDimapK1 l pab = lowerDimapK1 (l (liftDimapK1 pab))
112−
113−-- As expected
114−specDimapK1A :: (MProfunctor p) => p Int Int -> p Int Int
115−specDimapK1A = ravelDimapK1 testDimapK1A
116−
117−
118−
119−
120−-------------------------------------------------------------------------------
121−-- Pull Dimaps to the left
122−
123−data DimapSyntax p c d where
124− DMSMap :: Fun c a -> Fun b d -> p a b -> DimapSyntax p c d
125− DMSDirty :: p a b -> DimapSyntax p a b
126−
127−liftDimapSyntax :: p a b -> DimapSyntax p a b
128−liftDimapSyntax pab = DMSDirty pab
129−
130−lowerDimapSyntax :: MProfunctor p => DimapSyntax p a b -> p a b
131−lowerDimapSyntax (DMSDirty pab) = pab
132−lowerDimapSyntax (DMSMap pca pbd pab) = mdimap pca pbd pab
133−
134−instance MRight p => MRight (DimapSyntax p) where
135− mright (DMSDirty pab) = DMSDirty (mright pab)
136− mright (DMSMap fca fbd pab) = DMSMap (femap fca) (femap fbd) (mright pab)
137− {-# INLINE mright #-}
138−
139−-- Don't fuse here, we do that in DimapK1 at the end
140−instance MProfunctor p => MProfunctor (DimapSyntax p) where
141− mdimap f g (DMSDirty p) = DMSMap f g p
142− mdimap f g (DMSMap f' g' p) = DMSMap (f' <.> f) (g <.> g') p
143− {-# INLINE mdimap #-}
144−
145−f :: Fun Int (Either Int Int)
146−f = undefined
147−g :: Fun (Either Int Int) Int
148−g = undefined
149−
150−
151−testDimapSyntax :: (MProfunctor p, MRight p) => p Int Int -> p Int Int
152−testDimapSyntax pab = mdimap f g (mright (mdimap f g (mright pab)))
153−
154−ravelDimapSyntax :: (MProfunctor p, MRight p) =>
155− (DimapSyntax p a b -> DimapSyntax p s t)
156− -> p a b -> p s t
157−ravelDimapSyntax l pab = lowerDimapSyntax (l (liftDimapSyntax pab))
158−
159−ravelDimapSyntaxFF :: (MProfunctor p, MRight p) =>
160− (DimapSyntax (RightFuse (RightFreeTheorem p)) a b -> DimapSyntax (RightFuse (RightFreeTheorem p)) s t)
161− -> p a b -> p s t
162−ravelDimapSyntaxFF l pab = lowerRightFreeTheorem (lowerRightFuse (lowerDimapSyntax (l (liftDimapSyntax (liftRightFuse (liftRightFreeTheorem pab))))))
163−
164−-- Works
165−specDimapSyntax :: (MProfunctor p, MRight p) => p Int Int -> p Int Int
166−specDimapSyntax = ravelDimapSyntax testDimapSyntax
167−
168−
169−-------------------------------------------------------------------------------
170−-- Fuse together rights
171−{-
172−data RightSyntax p c d where
173− RSRight :: Fun c a -> Fun b d -> p a b -> DimapSyntax p c d
174− RSDirty :: p a b -> DimapSyntax p a b
175−
176−liftRightSyntax :: p a b -> RightSyntax p a b
177−liftRightSyntax pab = RSDirty pab
178−
179−lowerRightSyntax :: MRight p => RightSyntax p a b -> p a b
180−lowerRightSyntax (RSDirty pab) = pab
181−lowerRightSyntax (RSRight paa) = mright pa
182−
183−instance MRight p => MRight (DimapSyntax p) where
184− mright (DMSDirty pab) = DMSDirty (mright pab)
185− mright (DMSMap fca fbd pab) = DMSMap (ffmap fca) (ffmap fbd) (mright pab)
186−
187−-- Don't fuse here, we do that in DimapK1 at the end
188−instance MProfunctor p => MProfunctor (DimapSyntax p) where
189− mdimap f g (DMSDirty p) = DMSMap f g p
190− mdimap f g (DMSMap f' g' p) = DMSMap f g (mdimap f' g' p)
191− -}
192−-- We always want to ensure that "RightFuse p b c ~ p b c" So after
193−-- applying the "RightFuse" constructor when we encounter a right, we need
194−-- to ensure the type is "p (Either z b) (Either z c) as that's the type of
195−-- applying right
196−data RightFuse p b c where
197− RightFuse :: (forall a d z . Fun a (Either z b) -> Fun (Either z c) d -> p a d) -> RightFuse p (Either z b) (Either z c)
198− DirtyRightFuse :: p b c -> RightFuse p b c
199−
200−initRightFuse :: (MRight p, MProfunctor p) => p a b -> RightFuse p (Either z a) (Either z b)
201−initRightFuse pa = RightFuse (\f g -> mdimap f g (mright pa))
202−
203−liftRightFuse :: p a b -> RightFuse p a b
204−liftRightFuse pa = DirtyRightFuse pa
205−
206−lowerRightFuse :: RightFuse p a b -> p a b
207−lowerRightFuse (DirtyRightFuse pa) = pa
208−lowerRightFuse (RightFuse pa) = pa (liftFun id) (liftFun id)
209−
210−instance (MProfunctor p, MRight p) => MRight (RightFuse p) where
211− -- Both these work but this version maintains the EitherFun invariant
212− mright (RightFuse r) = RightFuse (\f1 f2 -> r (assocNew <.> f1) (f2 <.> assoc'New))
213−-- mright (RightFuse r) = RightFuse (\f1 f2 -> r (assoc <.> f1) (f2 <.> assoc'))
214− mright (DirtyRightFuse pab) = initRightFuse pab
215− {-# INLINE mright #-}
216−
217−-- Once we encounter a dimap we're done. As expressions are already in
218−-- a normal form. | We are here
219−-- |
220−-- dimap . dimap . dimap . <-| right . right . right
221−instance MProfunctor p => MProfunctor (RightFuse p) where
222− mdimap f g (RightFuse p) = DirtyRightFuse (p f g)
223−-- RightFuse (\aezb ezcd -> p (EitherFun Left (Right . lowerFun f) <.> aezb)
224−-- (ezcd <.> EitherFun Left (Right . lowerFun g)))
225−
226− -- We apply this adapther after floating all dimaps to the left so that
227− -- this should never happen
228− mdimap f g (DirtyRightFuse p) = DirtyRightFuse (mdimap f g p)
229− {-# INLINE mdimap #-}
230−
231−-- These are assoc and assoc' but with the free theorem applied
232−assocNew = WrappedFun (plus assocg id) <.> assoc
233−assoc'New = EitherFun id (\c -> Right (Right c))
234−
235−assocg = (\eab -> case eab of
236− Left a -> Left a
237− Right b -> Right (Left b))
238−
239−assoc :: Fun (Either a (Either b c)) (Either (Either a b) c)
240−assoc = EitherFun (\a -> Left (Left a))
241− (\ebc ->
242− case ebc of
243− Left b -> Left (Right b)
244− Right c -> Right c)
245−
246−assoc' :: Fun (Either (Either a b) c) (Either a (Either b c))
247−assoc' = EitherFun
248− (\eab -> case eab of
249− Left a -> Left a
250− Right b -> Right (Left b))
251− (\c -> Right (Right c) )
252−
253−-- Want to only have one mright
254−testRightFuse :: (MProfunctor p, MRight p) => p Int Int -> p (Either z2 (Either z (Either z1 Int))) (Either z2 ((Either z (Either z1 Int))))
255−testRightFuse pab = mright (mright (mright pab))
256−
257−ravelRightFuse :: (MProfunctor p, MRight p) =>
258− (RightFuse p a b -> RightFuse p s t)
259− -> p a b -> p s t
260−ravelRightFuse l pab = lowerRightFuse (l (liftRightFuse pab))
261−
262−-- Works
263−specRightFuse :: (MProfunctor p, MRight p) => p Int Int -> p (Either z2 (Either z (Either z1 Int))) (Either z2 ((Either z (Either z1 Int))))
264−specRightFuse = ravelRightFuse testRightFuse
265−
266−------------------------------------------------------------------------------
267−-- Then only start after a right
268−-- Don't think this is needed actually
269−{-
270−
271−data RightFuse1 p b c where
272− RightFuse1 :: p (Either z b) (Either z c) -> (RightFuse p (Either z b) (Either z c)) -> RightFuse1 p (Either z b) (Either z c)
273− DirtyRightFuse1 :: (p b c) -> RightFuse1 p b c
274−
275−initRightFuse1 :: (MRight p, MProfunctor p) => p a b -> RightFuse1 p (Either z a) (Either z b)
276−initRightFuse1 pab = RightFuse1 (mright pab) (initRightFuse pab)
277−
278−liftRightFuse1 = DirtyRightFuse1
279−
280−lowerRightFuse1 :: RightFuse1 p a b -> p a b
281−lowerRightFuse1 (RightFuse1 pab _) = pab
282−lowerRightFuse1 (DirtyRightFuse1 pbc) = pbc
283−
284−rfdimap :: Fun c a -> Fun b d -> RightFuse p a b -> p c d
285−rfdimap f g (RightFuse rf) = rf f g
286−
287−-- Once we see a dimap, it's over, remember the normal form invariant
288−instance MProfunctor p => MProfunctor (RightFuse1 p) where
289− mdimap f g (RightFuse1 _ d) = DirtyRightFuse1 (rfdimap f g d) -- RightFuse1 (rfdimap f g d) (mdimap f g d)
290− mdimap f g (DirtyRightFuse1 pbc) = DirtyRightFuse1 (mdimap f g pbc)
291−
292−instance (MProfunctor p, MRight p) => MRight (RightFuse1 p) where
293− mright (DirtyRightFuse1 pbc) = initRightFuse1 pbc
294− mright (RightFuse1 _ d) = RightFuse1 (mright (lowerRightFuse d)) (mright d)
295−
296−-- Want to only have one mright
297−testRightFuse1 :: (MProfunctor p, MRight p) => p Int Int -> p (Either z (Either z1 Int)) ((Either z (Either z1 Int)))
298−testRightFuse1 pab = mright (mright pab)
299−
300−ravelRightFuse1 :: (MProfunctor p, MRight p) =>
301− (RightFuse1 p a b -> RightFuse1 p s t)
302− -> p a b -> p s t
303−ravelRightFuse1 l pab = lowerRightFuse1 (l (liftRightFuse1 pab))
304−
305−-- Fails
306−specRightFuse1 :: (MProfunctor p, MRight p) => p Int Int -> p (Either z (Either z1 Int)) ((Either z (Either z1 Int)))
307−specRightFuse1 = ravelRightFuse1 testRightFuse1
308−-}
309−------------------------------------------------------------------------------
310−-- Now apply a specialised version of the free theorem, do this before
311−-- fusing anything or we risk losing information
312−-- dimap f (either g h) (right p) = dimap (either g f) (either id h) (right p)
313−
314−data RightFreeTheorem p a b where
315− RFRight :: p a b -> RightFreeTheorem p (Either z a) (Either z b)
316−-- RFDMap :: Fun c a -> Fun b d -> p a b -> RightFreeTheorem p c d
317− RFDirty :: p a b -> RightFreeTheorem p a b
318−
319−liftRightFreeTheorem :: p a b -> RightFreeTheorem p a b
320−liftRightFreeTheorem = RFDirty
321−
322−lowerRightFreeTheorem :: (MProfunctor p, MRight p) => RightFreeTheorem p a b -> p a b
323−lowerRightFreeTheorem (RFDirty pab) = pab
324−--lowerRightFreeTheorem (RFDMap fa fb pab) = mdimap fa fb pab
325−lowerRightFreeTheorem (RFRight pab) = mright pab
326−
327−instance (MRight p, MProfunctor p) => MProfunctor (RightFreeTheorem p) where
328− -- We are interested in dimap ... (right so dimap after a dirty is
329− -- pointless so just delegate
330− mdimap f g (RFDirty pab) = RFDirty (mdimap f g pab)
331− -- Actually I we need to do fusion in the same pass otherwise we just do
332− -- this for dimaps immediately next to a right BUT we do a partial fusion
333− -- pass first which spots these opporunities
334− mdimap f (EitherFun g h) (RFRight pab) = RFDirty (mdimap (liftFun (plus g id) <.> f) (EitherFun id h) (mright pab))
335− mdimap f g (RFRight pab) = RFDirty (mdimap f g (mright pab))
336− {-# INLINE mdimap #-}
337−
338−instance MRight p => MRight (RightFreeTheorem p) where
339− mright (RFDirty pab) = RFRight pab
340− mright (RFRight pab) = RFRight (mright pab)
341− {-# INLINE mright #-}
342−
343−
344−plus :: (a -> b) -> (c -> d) -> Either a c -> Either b d
345−plus f g (Left x) = Left (f x)
346−plus f g (Right x) = Right (g x)
347−
348−
349−-- Want to only have one mright
350−testRightFreeTheorem :: (MProfunctor p, MRight p) => p Int Int -> p Int Int
351−testRightFreeTheorem pab = mdimap f g (mright (mdimap f g (mright pab)))
352− where
353− f :: Fun Int (Either Int Int)
354− f = liftFun Left
355−
356− g :: Fun (Either Int Int) Int
357− g = EitherFun (const 0) (const 0)
358−
359−ravelRightFreeTheorem :: (MProfunctor p, MRight p) =>
360− (RightFreeTheorem p a b -> RightFreeTheorem p s t)
361− -> p a b -> p s t
362−ravelRightFreeTheorem l pab = lowerRightFreeTheorem (l (liftRightFreeTheorem pab))
363−
364−-- Works
365−specRightFreeTheorem :: (MProfunctor p, MRight p) => p Int Int -> p Int Int
366−specRightFreeTheorem = ravelRightFreeTheorem testRightFreeTheorem
367−
368−------------------------------------------------------------------------------
369−-- A partial dimap fusion pass which only fuses functions such that
370−-- we still have an EitherFun in the 2nd argument of dimap.
371−{-
372−data PartialFusion p a b where
373− PFDMap :: Fun c a -> Fun b d -> p a b -> PartialFusion p c d
374− PFDirty :: p a b -> PartialFusion p a b
375−
376−liftPartialFusion :: p a b -> PartialFusion p a b
377−liftPartialFusion = PFDirty
378−
379−lowerPartialFusion :: MProfunctor p => PartialFusion p a b -> p a b
380−lowerPartialFusion (PFDirty pab) = pab
381−lowerPartialFusion (PFDMap fca fbd pcd) = mdimap fca fbd pcd
382−
383−instance (MRight p, MProfunctor p) => MProfunctor (PartialFusion p) where
384− mdimap f g (PFDirty pab) = PFDMap f g pab
385− mdimap f g (PFDMap f' (EitherFun l r) pab) = PFDMap (f' <.> f) (EitherFun ((lowerFun g) . l) ((lowerFun g) . r)) pab
386− mdimap f g (PFDMap f' g' pab) = PFDMap f g (mdimap f' g' pab)
387−
388−instance (MProfunctor p, MRight p) => MRight (PartialFusion p) where
389− mright pab = PFDirty (mright (lowerPartialFusion pab))
390−
391−
392−-- We want g and g' to fuse but not the innermost dimap so we are left with
393−-- two dimaps
394−testPartialFusion :: (MProfunctor p, MRight p) => p Int Int -> p Int Int
395−testPartialFusion pab = (mdimap id' g (mdimap (WrappedFun Left) g' (mdimap id' id' (mright pab))))
396− where
397− id' = WrappedFun id
398−
399− g' :: Fun (Either Int Int) Int
400− g' = EitherFun (const 0) (const 0)
401−
402− g :: Fun Int Int
403− g = WrappedFun (const 2)
404−
405−
406−ravelPartialFusion :: (MProfunctor p, MRight p) =>
407− (PartialFusion p a b -> PartialFusion p s t)
408− -> p a b -> p s t
409−ravelPartialFusion l pab = lowerPartialFusion (l (liftPartialFusion pab))
410−
411−-- Works
412−specPartialFusion :: (MProfunctor p, MRight p) => p Int Int -> p Int Int
413−specPartialFusion = ravelPartialFusion testPartialFusion
414−-}
415−------------------------------------------------------------------------------
416−-- We also lift the function space so that we can rewrite
417−-- dimap f (either g h) (right r) = dimap (either g f) (either id h) (right r)
418−--
419−data Fun a b where
420− WrappedFun :: (a -> b) -> Fun a b
421−-- PlusFun :: (x1 -> x2) -> (y1 -> y2) -> Fun (Either x1 y1) (Either x2 y2)
422− EitherFun :: (x1 -> z) -> (x2 -> z) -> Fun (Either x1 x2) z
423−
424−lowerFun :: Fun a b -> a -> b
425−lowerFun (WrappedFun f) = f
426−lowerFun (EitherFun l r) = either l r
427−
428−liftFun :: (a -> b) -> Fun a b
429−liftFun = WrappedFun
430−
431−ffmap :: Functor f => Fun a b -> Fun (f a) (f b)
432−ffmap fab = liftFun (fmap (lowerFun fab))
433−
434−ddimap :: MProfunctor p => (c -> a) -> (b -> d) -> p a b -> p c d
435−ddimap f g = mdimap (liftFun f) (liftFun g)
436−
437−femap :: Fun a b -> Fun (Either z a) (Either z b)
438−femap f = EitherFun (Left . id) (Right . lowerFun f)
439−
440−-- Note that (<.>) never destroys an inner EitherFun, that is good
441−-- for the free theorem as it means we are free to fuse dimaps whenever
442−-- we like without fear of destroying this chance. For if we had the chance
443−-- before, we still will afterwards.
444−(<.>) :: Fun b c -> Fun a b -> Fun a c
445−(<.>) cb (EitherFun l r) = EitherFun (lowerFun cb . l) (lowerFun cb . r)
446−(<.>) f g = liftFun (lowerFun f . lowerFun g)
447−
448−(<.) :: Fun b c -> (a -> b) -> Fun a c
449−(<.) f g = f <.> liftFun g
450−
451−(.>) :: (b -> c) -> Fun a b -> Fun a c
452−(.>) f g = liftFun f <.> g
453−
454−-------------------------------------------------------------------------------
455−
456−class MProfunctor p where
457− mdimap :: Fun c a -> Fun b d -> p a b -> p c d
458−
459−class MRight p where
460− mright :: p x y -> p (Either z x) (Either z y)
461−
462−
463−-------------------------------------------------------------------------------
464−-- Note [Optimisating Prisms Plan]
465−-- We want to write prisms in a normal form of
466−-- dimap f (EitherFun id g) . right
467−--
468−-- To that end, the optimisation plan is
469−--
470−-- 1. Float dimaps to the left and rights to the right, fusing together
471−-- dimaps as we go.
472−-- 2. Fuse together rights (taking care not to introduce EitherFun with
473−-- non-id first argument
474−-- 3. Apply the right free theorem to replace EitherFun f g with EitherFun
475−-- id g
476−--
477−-- There are three different adapters for this purpose
478−--
479−-- 1. DimapSyntax
480−-- 2. RightFuse
481−-- 3. RightFreeTheorem
482−--
483−-------------------------------------------------------------------------------
484−-- Debugging help
485−
486−-- Run each stage individually
487−
488−stage1, stage3, stage4, stage5 :: (MProfunctor p, MRight p) => (forall p . (MProfunctor p, MRight p) => p a b) -> p a b
489−stage1 = lowerDimapSyntax
490−--stage2 = lowerPartialFusion
491−stage3 = lowerRightFreeTheorem
492−stage4 = lowerRightFuse
493−stage5 = lowerDimapK1
494−
495−ravelStage1, ravelStage3, ravelStage4, ravelStage5, ravelStage3a
496− :: (MProfunctor p, MRight p)
497− => (forall p . (MProfunctor p, MRight p) => p a b -> p s t) ->
498− p a b -> p s t
499−
500−ravelStage1 = ravelDimapSyntax
501−--ravelStage2 = ravelPartialFusion
502−ravelStage3 = ravelRightFreeTheorem
503−ravelStage3a = ravelDimapSyntaxFF
504−ravelStage4 = ravelRightFuse
505−ravelStage5 = ravelDimapK1
506−
507−newtype FusionStack p a b =
508− FusionStack { getFusionStack :: (DimapSyntax (RightFuse (RightFreeTheorem p)) a b) }
509−
510−instance (MRight p, MProfunctor p) => MProfunctor (FusionStack p) where
511− mdimap f g pab = FusionStack (mdimap f g (getFusionStack pab))
512− {-# INLINE mdimap #-}
513−
514−instance (MProfunctor p, MRight p) => MRight (FusionStack p) where
515− mright pab = FusionStack (mright (getFusionStack pab))
516− {-# INLINE mright #-}
517−
518−liftFusionStack :: MProfunctor p => p a b -> FusionStack p a b
519−liftFusionStack = FusionStack . liftDimapSyntax . liftRightFuse . liftRightFreeTheorem
520−
521−lowerFusionStack :: (MProfunctor p, MRight p) => FusionStack p a b -> p a b
522−lowerFusionStack = lowerRightFreeTheorem . lowerRightFuse . lowerDimapSyntax . getFusionStack
523−{-# INLINE lowerFusionStack #-}
524−
525−ravelFusionStack :: (MProfunctor p, MRight p) =>
526− (FusionStack p a b -> FusionStack p s t)
527− -> p a b -> p s t
528−ravelFusionStack l pab = lowerFusionStack (l (liftFusionStack pab))
529−{-# INLINE ravelFusionStack #-}
530−
531−{- This is morally correct but it doesn't optimise well,
532−- it is also pointless doing it like this as it introduces a lot of
533−- unecessary fmaps which we have to fuse together
534−ravelVLFusionStack :: (MProfunctor p, MRight p, Applicative f) =>
535− ((WrappedVLProfunctor (Boggle f) (FusionStack p)) a b
536− -> ((WrappedVLProfunctor (Boggle f) (FusionStack p)) s t))
537− -> p a (f b) -> p s (f t)
538−ravelVLFusionStack l pab =
539− (lowerFusionStack (ddimap id lowerBoggle (wrappedVLProfunctor (
540− (l (WrappedVLProfunctor (ddimap id liftBoggle (liftFusionStack pab))))))))
541−{-# INLINE ravelVLFusionStack #-}
542−-}
543−
544−-- Works
545−--specFusionStack1 :: (MProfunctor p, MRight p) => p Int Int -> p Int Int
546−--specFusionStack1 pab = ravelFusionStack testPartialFusion pab
547−
548−specFusionStack2 :: (MProfunctor p, MRight p) => p Int Int -> p Int Int
549−specFusionStack2 pab = ravelFusionStack testRightFreeTheorem pab
550−
551−-------------------------------------------------------------------------------
552−-- A wrapped profunctor which we can make an instance of our classes.
553−--
554−
555−-- Use this adapter to get a profunctor lens
556−newtype WrappedProfunctor p a b = WrappedProfunctor { wrappedProfunctor :: (p a b) }
557−
558−instance Profunctor p => MProfunctor (WrappedProfunctor p) where
559− mdimap f g (WrappedProfunctor p) = WrappedProfunctor (dimap (lowerFun f) (lowerFun g) p)
560− {-# INLINE mdimap #-}
561−
562−instance Choice p => MRight (WrappedProfunctor p) where
563− mright (WrappedProfunctor p) = WrappedProfunctor (right' p)
564− {-# INLINE mright #-}
565−
566−-- | Use this adapter to get a VL lens
567−newtype WrappedVLMProfunctor f p a b
568− = WrappedVLMProfunctor { wrappedVLMProfunctor :: p a (f b) }
569−
570−instance (Functor f, MProfunctor p) => MProfunctor (WrappedVLMProfunctor f p) where
571− mdimap f g (WrappedVLMProfunctor pafb)
572− = WrappedVLMProfunctor (mdimap f (ffmap g) pafb)
573− {-# INLINE mdimap #-}
574−
575−instance (Applicative f, MRight p, MProfunctor p) => MRight (WrappedVLMProfunctor f p) where
576− mright (WrappedVLMProfunctor pafb)
577− = WrappedVLMProfunctor (mdimap (liftFun id) (EitherFun (pure . Left) (fmap Right)) (mright pafb))
578− {-# INLINE mright #-}
579−
580−{-
581−-- | Use this adapter to get a VL lens
582−newtype WrappedVLMProfunctor f p a b
583− = WrappedVLMProfunctor { wrappedVLProfunctor :: p a (f b) }
584−
585−instance (Functor f, MProfunctor p) => MProfunctor (WrappedVLMProfunctor f p) where
586− mdimap f g (WrappedVLProfunctor pafb)
587− = WrappedVLProfunctor (mdimap f (ffmap g) pafb)
588− {-# INLINE mdimap #-}
589−
590−instance (Applicative f, MRight p, MProfunctor p) => MRight (WrappedVLMProfunctor f p) where
591− mright (WrappedVLMProfunctor pafb)
592− = WrappedVLMProfunctor (ddimap id (either (pure . Left) (fmap Right)) (mright pafb))
593− {-# INLINE mright #-}
594− -}
595−
596−
597−
598−{-
599−instance MoreProfunctor p => MoreProfunctor (MoreBoggle p) where
600− mdimap f g (MDirty pab) = MDMap f g pab
601− mdimap f g (MDMap f' g' pab) = MDMap (f' <.> f) (g <.> g') pab
602− mdimap f (EitherFun l r) (MDRight pab)
603− = MDMap (liftFun (plus l id) <.> f) (EitherFun id r) (mright pab)
604− mdimap f g (MDRight pab) = MDMap f g (mright pab)
605−
606− mright (MDirty pab) = MDRight pab
607− mright (MDMap f g pab) = MDMap (ffmap f) (ffmap g) (mright pab)
608− mright (MDRight pab) = MDMap assoc assoc' (mright pab)
609−
610−
611−
612−data WInt = Wrap { unWrap :: !Int }
613−
614−--test :: MoreProfunctor p => p Int Int -> p (Either WInt Int) (Either WInt Int)
615−--test pii = mdimap (PlusFun unWrap id) (liftFun id) (mplusp Wrap (mright pii))
616−
617−test :: MoreProfunctor p => p Int Int -> p WInt WInt
618−test pii = mdimap (liftFun too) (EitherFun Wrap Wrap) (mright pii)
619− where
620− too :: WInt -> Either Int Int
621− too (Wrap 0) = Left 0
622− too (Wrap n) = Right n
623−
624−spec1 :: forall p . Choice p => p Int Int -> p WInt WInt
625−spec1 eta = wrappedProfunctor (test (WrappedProfunctor eta))
626−
627−spec :: Choice p => p Int Int -> p WInt WInt
628−spec eta = prismRavel test eta
629−
630−injPrism :: MoreProfunctor p => MoreBoggle p s t -> p s t
631−injPrism (MDRight pa) = mright pa
632−injPrism (MDirty pa) = pa
633−injPrism (MDMap f g pa) = mdimap f g pa
634−projPrism = MDirty
635−
636−
637−prismRavel :: (Choice p)
638− => (MoreBoggle (WrappedProfunctor p) a b -> MoreBoggle (WrappedProfunctor p) s t)
639− -> (p a b -> p s t)
640−prismRavel coy pafb = wrappedProfunctor (injPrism (coy (projPrism (WrappedProfunctor pafb))))
641−{-# INLINE prismRavel #-}
642−-}
643−
644−
645−
stack.yaml+1−4
@@ -1,11 +1,8 @@
1−resolver: lts-9.2
1+resolver: nightly-2018-08-09
2
2
3packages:
3packages:
4- '.'
4- '.'
5
5
6−extra-deps:
7−- 'inspection-testing-0.1.1.2'
8−
9flags: {}
6flags: {}
10
7
11extra-package-dbs: []
8extra-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+165−45
@@ -1,6 +1,8 @@
1−{-# OPTIONS_GHC -O -fplugin Test.Inspection.Plugin -dsuppress-all #-}
1+{-# OPTIONS_GHC -O -fplugin Test.Inspection.Plugin #-}
2+{-# OPTIONS_GHC -dsuppress-all #-}
2
3
3{-# LANGUAGE AllowAmbiguousTypes #-}
4{-# LANGUAGE AllowAmbiguousTypes #-}
5+{-# LANGUAGE CPP #-}
4{-# LANGUAGE DataKinds #-}
6{-# LANGUAGE DataKinds #-}
5{-# LANGUAGE DeriveGeneric #-}
7{-# LANGUAGE DeriveGeneric #-}
6{-# LANGUAGE DuplicateRecordFields #-}
8{-# LANGUAGE DuplicateRecordFields #-}
@@ -9,21 +11,36 @@
9{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE TypeApplications #-}
12{-# LANGUAGE TypeApplications #-}
11{-# LANGUAGE TemplateHaskell #-}
13{-# LANGUAGE TemplateHaskell #-}
12−-- For the VL prism test
14+{-# LANGUAGE OverloadedLabels #-}
13−{-# OPTIONS_GHC -funfolding-use-threshold=70 #-}
14
15
15module Main where
16module Main where
16
17
17import GHC.Generics
18import GHC.Generics
18−import Data.Profunctor
19import Data.Generics.Product
19import Data.Generics.Product
20−import Data.Generics.Internal.Lens (prismPRavel)
21import Data.Generics.Sum
20import Data.Generics.Sum
22−import Data.Generics.Product.Boggle
23import Test.Inspection
21import 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)
24
37
25main :: IO ()
38main :: IO ()
26−main = putStrLn "Hello world"
39+main = do
40+ res <- runTestTT tests
41+ case errors res + failures res of
42+ 0 -> exitSuccess
43+ _ -> exitFailure
27
44
28data Record = MkRecord
45data Record = MkRecord
29 { fieldA :: Int
46 { fieldA :: Int
@@ -39,23 +56,6 @@ data Record3 a = MkRecord3
39 , fieldB :: Bool
56 , fieldB :: Bool
40 } deriving (Generic, Show)
57 } deriving (Generic, Show)
41
58
42−type Lens' s a = Lens s s a a
43−type Prism' s a = Prism s s a a
44−type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
45−type Prism s t a b =
46− forall p f . (Choice p, Applicative f) => p a (f b) -> p s (f t)
47−
48−type PrismP s t a b =
49− forall p . (Choice p) => p a b -> p s t
50−type Traversal' s a = forall f. Applicative f => (a -> f a) -> s -> f s
51−
52−prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
53−prism bt seta eta = dimap (\x -> plus pure id (seta x)) (either id (\x -> fmap bt x)) (right' eta)
54−{-# INLINE prism #-}
55−
56−plus f g (Left x) = Left (f x)
57−plus f g (Right y) = Right (g y)
58−
59data Record4 a = MkRecord4
59data Record4 a = MkRecord4
60 { fieldA :: a
60 { fieldA :: a
61 , fieldB :: a
61 , fieldB :: a
@@ -70,8 +70,6 @@ data Record5 = MkRecord5
70 , fieldF :: Int
70 , fieldF :: Int
71 } deriving Generic
71 } deriving Generic
72
72
73−
74−
75typeChangingManual :: Lens (Record3 a) (Record3 b) a b
73typeChangingManual :: Lens (Record3 a) (Record3 b) a b
76typeChangingManual f (MkRecord3 a b) = (\a' -> MkRecord3 a' b) <$> f a
74typeChangingManual f (MkRecord3 a b) = (\a' -> MkRecord3 a' b) <$> f a
77
75
@@ -82,15 +80,11 @@ newtype L s a = L (Lens' s a)
82
80
83intTraversalManual :: Traversal' Record5 Int
81intTraversalManual :: Traversal' Record5 Int
84intTraversalManual f (MkRecord5 a b c d e f') =
82intTraversalManual f (MkRecord5 a b c d e f') =
85− (\a1 a2 a3 a4 -> MkRecord5 a1 a2 c a3 e a4) <$> f a <*> f b <*> f d <*> f f'
83+ pure (\a1 a2 a3 a4 -> MkRecord5 a1 a2 c a3 e a4) <*> f a <*> f b <*> f d <*> f f'
86
84
87intTraversalDerived :: Traversal' Record5 Int
85intTraversalDerived :: Traversal' Record5 Int
88intTraversalDerived = types
86intTraversalDerived = types
89
87
90−
91−intTraversalDerived3 :: Traversal' (Record4 Int) Int
92−intTraversalDerived3 = genericTraverse
93−
94fieldALensManual :: Lens' Record Int
88fieldALensManual :: Lens' Record Int
95fieldALensManual f (MkRecord a b) = (\a' -> MkRecord a' b) <$> f a
89fieldALensManual f (MkRecord a b) = (\a' -> MkRecord a' b) <$> f a
96
90
@@ -101,6 +95,37 @@ subtypeLensManual f record
101 ) (f (MkRecord2 (case record of {MkRecord a _ -> a})))
95 ) (f (MkRecord2 (case record of {MkRecord a _ -> a})))
102
96
103data Sum1 = A Char | B Int | C () | D () deriving (Generic, Show)
97data 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
104
129
105sum1PrismManual :: Prism Sum1 Sum1 Int Int
130sum1PrismManual :: Prism Sum1 Sum1 Int Int
106sum1PrismManual eta = prism g f eta
131sum1PrismManual eta = prism g f eta
@@ -110,6 +135,45 @@ sum1PrismManual eta = prism g f eta
110 s -> Left s
135 s -> Left s
111 g = B
136 g = B
112
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+
113
177
114--------------------------------------------------------------------------------
178--------------------------------------------------------------------------------
115-- * Tests
179-- * Tests
@@ -121,36 +185,92 @@ sum1PrismManual eta = prism g f eta
121fieldALensName :: Lens' Record Int
185fieldALensName :: Lens' Record Int
122fieldALensName = field @"fieldA"
186fieldALensName = field @"fieldA"
123
187
188+fieldALensName_ :: Lens' Record Int
189+fieldALensName_ = field_ @"fieldA"
190+
124fieldALensType :: Lens' Record Int
191fieldALensType :: Lens' Record Int
125fieldALensType = typed @Int
192fieldALensType = typed @Int
126
193
127fieldALensPos :: Lens' Record Int
194fieldALensPos :: Lens' Record Int
128fieldALensPos = position @1
195fieldALensPos = position @1
129
196
197+fieldALensPos_ :: Lens' Record Int
198+fieldALensPos_ = position_ @1
199+
130subtypeLensGeneric :: Lens' Record Record2
200subtypeLensGeneric :: Lens' Record Record2
131subtypeLensGeneric = super
201subtypeLensGeneric = super
132
202
133typeChangingGeneric :: Lens (Record3 a) (Record3 b) a b
203typeChangingGeneric :: Lens (Record3 a) (Record3 b) a b
134−typeChangingGeneric = field @"fieldA"
204+typeChangingGeneric = #fieldA
135
205
136typeChangingGenericPos :: Lens (Record3 a) (Record3 b) a b
206typeChangingGenericPos :: Lens (Record3 a) (Record3 b) a b
137typeChangingGenericPos = position @1
207typeChangingGenericPos = position @1
138
208
139typeChangingGenericCompose :: Lens (Record3 (Record3 a)) (Record3 (Record3 b)) a b
209typeChangingGenericCompose :: Lens (Record3 (Record3 a)) (Record3 (Record3 b)) a b
140−typeChangingGenericCompose = field @"fieldA" . field @"fieldA"
210+typeChangingGenericCompose = #fieldA . #fieldA
211+
212+typeChangingGenericCompose_ :: Lens (Record3 (Record3 a)) (Record3 (Record3 b)) a b
213+typeChangingGenericCompose_ = field_ @"fieldA" . field_ @"fieldA"
141
214
142sum1PrismB :: Prism Sum1 Sum1 Int Int
215sum1PrismB :: Prism Sum1 Sum1 Int Int
143sum1PrismB = _Ctor @"B"
216sum1PrismB = _Ctor @"B"
144
217
145−sum1PrismBP :: PrismP Sum1 Sum1 Int Int
218+subtypePrismGeneric :: Prism Sum1 Sum1 Sum2 Sum2
146−sum1PrismBP = prismPRavel (_CtorRaw @"B")
219+subtypePrismGeneric = _Sub
147−
220+
148−inspect $ 'fieldALensManual === 'fieldALensName
221+sum1TypePrism :: Prism Sum1 Sum1 Int Int
149−inspect $ 'fieldALensManual === 'fieldALensType
222+sum1TypePrism = _Typed @Int
150−inspect $ 'fieldALensManual === 'fieldALensPos
223+
151−inspect $ 'subtypeLensManual === 'subtypeLensGeneric
224+sum1TypePrismChar :: Prism Sum1 Sum1 Char Char
152−inspect $ 'typeChangingManual === 'typeChangingGeneric
225+sum1TypePrismChar = _Typed @Char
153−inspect $ 'typeChangingManual === 'typeChangingGenericPos
226+
154−inspect $ 'typeChangingManualCompose === 'typeChangingGenericCompose
227+sum2TypePrism :: Prism Sum2 Sum2 Int Int
155−inspect $ 'intTraversalManual === 'intTraversalDerived
228+sum2TypePrism = _Typed @Int
156−inspect $ 'sum1PrismManual === 'sum1PrismB
229+
230+sum2TypePrismChar :: Prism Sum2 Sum2 Char Char
231+sum2TypePrismChar = _Typed @Char
232+
233+data SumOfProducts =
234+ RecA { _foo :: Int, valA :: String }
235+ | RecB { _foo :: Int, valB :: Bool }
236+ | RecC { _foo :: Int }
237+ deriving (Show, Eq, Generic)
238+
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
275+
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.hs+1
@@ -4,4 +4,5 @@ main
4 [ "-isrc"
4 [ "-isrc"
5 , "src/Data/Generics/Product.hs"
5 , "src/Data/Generics/Product.hs"
6 , "src/Data/Generics/Sum.hs"
6 , "src/Data/Generics/Sum.hs"
7+ , "src/Data/Generics/Labels.hs"
7 ]
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 _ = []