packages feed

prolens (empty) → 0.0.0.0

raw patch · 11 files changed

+1985/−0 lines, 11 filesdep +basedep +doctestdep +hedgehog

Dependencies added: base, doctest, hedgehog, hspec, hspec-hedgehog, inspection-testing, prolens

Files

+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog++`prolens` uses [PVP Versioning][1].+The changelog is available [on GitHub][2].++## 0.0.0.0++* Initially created.++[1]: https://pvp.haskell.org+[2]: https://github.com/kowainik/prolens/releases
+ LICENSE view
@@ -0,0 +1,373 @@+Mozilla Public License Version 2.0+==================================++1. Definitions+--------------++1.1. "Contributor"+    means each individual or legal entity that creates, contributes to+    the creation of, or owns Covered Software.++1.2. "Contributor Version"+    means the combination of the Contributions of others (if any) used+    by a Contributor and that particular Contributor's Contribution.++1.3. "Contribution"+    means Covered Software of a particular Contributor.++1.4. "Covered Software"+    means Source Code Form to which the initial Contributor has attached+    the notice in Exhibit A, the Executable Form of such Source Code+    Form, and Modifications of such Source Code Form, in each case+    including portions thereof.++1.5. "Incompatible With Secondary Licenses"+    means++    (a) that the initial Contributor has attached the notice described+        in Exhibit B to the Covered Software; or++    (b) that the Covered Software was made available under the terms of+        version 1.1 or earlier of the License, but not also under the+        terms of a Secondary License.++1.6. "Executable Form"+    means any form of the work other than Source Code Form.++1.7. "Larger Work"+    means a work that combines Covered Software with other material, in+    a separate file or files, that is not Covered Software.++1.8. "License"+    means this document.++1.9. "Licensable"+    means having the right to grant, to the maximum extent possible,+    whether at the time of the initial grant or subsequently, any and+    all of the rights conveyed by this License.++1.10. "Modifications"+    means any of the following:++    (a) any file in Source Code Form that results from an addition to,+        deletion from, or modification of the contents of Covered+        Software; or++    (b) any new file in Source Code Form that contains any Covered+        Software.++1.11. "Patent Claims" of a Contributor+    means any patent claim(s), including without limitation, method,+    process, and apparatus claims, in any patent Licensable by such+    Contributor that would be infringed, but for the grant of the+    License, by the making, using, selling, offering for sale, having+    made, import, or transfer of either its Contributions or its+    Contributor Version.++1.12. "Secondary License"+    means either the GNU General Public License, Version 2.0, the GNU+    Lesser General Public License, Version 2.1, the GNU Affero General+    Public License, Version 3.0, or any later versions of those+    licenses.++1.13. "Source Code Form"+    means the form of the work preferred for making modifications.++1.14. "You" (or "Your")+    means an individual or a legal entity exercising rights under this+    License. For legal entities, "You" includes any entity that+    controls, is controlled by, or is under common control with You. For+    purposes of this definition, "control" means (a) the power, direct+    or indirect, to cause the direction or management of such entity,+    whether by contract or otherwise, or (b) ownership of more than+    fifty percent (50%) of the outstanding shares or beneficial+    ownership of such entity.++2. License Grants and Conditions+--------------------------------++2.1. Grants++Each Contributor hereby grants You a world-wide, royalty-free,+non-exclusive license:++(a) under intellectual property rights (other than patent or trademark)+    Licensable by such Contributor to use, reproduce, make available,+    modify, display, perform, distribute, and otherwise exploit its+    Contributions, either on an unmodified basis, with Modifications, or+    as part of a Larger Work; and++(b) under Patent Claims of such Contributor to make, use, sell, offer+    for sale, have made, import, and otherwise transfer either its+    Contributions or its Contributor Version.++2.2. Effective Date++The licenses granted in Section 2.1 with respect to any Contribution+become effective for each Contribution on the date the Contributor first+distributes such Contribution.++2.3. Limitations on Grant Scope++The licenses granted in this Section 2 are the only rights granted under+this License. No additional rights or licenses will be implied from the+distribution or licensing of Covered Software under this License.+Notwithstanding Section 2.1(b) above, no patent license is granted by a+Contributor:++(a) for any code that a Contributor has removed from Covered Software;+    or++(b) for infringements caused by: (i) Your and any other third party's+    modifications of Covered Software, or (ii) the combination of its+    Contributions with other software (except as part of its Contributor+    Version); or++(c) under Patent Claims infringed by Covered Software in the absence of+    its Contributions.++This License does not grant any rights in the trademarks, service marks,+or logos of any Contributor (except as may be necessary to comply with+the notice requirements in Section 3.4).++2.4. Subsequent Licenses++No Contributor makes additional grants as a result of Your choice to+distribute the Covered Software under a subsequent version of this+License (see Section 10.2) or under the terms of a Secondary License (if+permitted under the terms of Section 3.3).++2.5. Representation++Each Contributor represents that the Contributor believes its+Contributions are its original creation(s) or it has sufficient rights+to grant the rights to its Contributions conveyed by this License.++2.6. Fair Use++This License is not intended to limit any rights You have under+applicable copyright doctrines of fair use, fair dealing, or other+equivalents.++2.7. Conditions++Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted+in Section 2.1.++3. Responsibilities+-------------------++3.1. Distribution of Source Form++All distribution of Covered Software in Source Code Form, including any+Modifications that You create or to which You contribute, must be under+the terms of this License. You must inform recipients that the Source+Code Form of the Covered Software is governed by the terms of this+License, and how they can obtain a copy of this License. You may not+attempt to alter or restrict the recipients' rights in the Source Code+Form.++3.2. Distribution of Executable Form++If You distribute Covered Software in Executable Form then:++(a) such Covered Software must also be made available in Source Code+    Form, as described in Section 3.1, and You must inform recipients of+    the Executable Form how they can obtain a copy of such Source Code+    Form by reasonable means in a timely manner, at a charge no more+    than the cost of distribution to the recipient; and++(b) You may distribute such Executable Form under the terms of this+    License, or sublicense it under different terms, provided that the+    license for the Executable Form does not attempt to limit or alter+    the recipients' rights in the Source Code Form under this License.++3.3. Distribution of a Larger Work++You may create and distribute a Larger Work under terms of Your choice,+provided that You also comply with the requirements of this License for+the Covered Software. If the Larger Work is a combination of Covered+Software with a work governed by one or more Secondary Licenses, and the+Covered Software is not Incompatible With Secondary Licenses, this+License permits You to additionally distribute such Covered Software+under the terms of such Secondary License(s), so that the recipient of+the Larger Work may, at their option, further distribute the Covered+Software under the terms of either this License or such Secondary+License(s).++3.4. Notices++You may not remove or alter the substance of any license notices+(including copyright notices, patent notices, disclaimers of warranty,+or limitations of liability) contained within the Source Code Form of+the Covered Software, except that You may alter any license notices to+the extent required to remedy known factual inaccuracies.++3.5. Application of Additional Terms++You may choose to offer, and to charge a fee for, warranty, support,+indemnity or liability obligations to one or more recipients of Covered+Software. However, You may do so only on Your own behalf, and not on+behalf of any Contributor. You must make it absolutely clear that any+such warranty, support, indemnity, or liability obligation is offered by+You alone, and You hereby agree to indemnify every Contributor for any+liability incurred by such Contributor as a result of warranty, support,+indemnity or liability terms You offer. You may include additional+disclaimers of warranty and limitations of liability specific to any+jurisdiction.++4. Inability to Comply Due to Statute or Regulation+---------------------------------------------------++If it is impossible for You to comply with any of the terms of this+License with respect to some or all of the Covered Software due to+statute, judicial order, or regulation then You must: (a) comply with+the terms of this License to the maximum extent possible; and (b)+describe the limitations and the code they affect. Such description must+be placed in a text file included with all distributions of the Covered+Software under this License. Except to the extent prohibited by statute+or regulation, such description must be sufficiently detailed for a+recipient of ordinary skill to be able to understand it.++5. Termination+--------------++5.1. The rights granted under this License will terminate automatically+if You fail to comply with any of its terms. However, if You become+compliant, then the rights granted under this License from a particular+Contributor are reinstated (a) provisionally, unless and until such+Contributor explicitly and finally terminates Your grants, and (b) on an+ongoing basis, if such Contributor fails to notify You of the+non-compliance by some reasonable means prior to 60 days after You have+come back into compliance. Moreover, Your grants from a particular+Contributor are reinstated on an ongoing basis if such Contributor+notifies You of the non-compliance by some reasonable means, this is the+first time You have received notice of non-compliance with this License+from such Contributor, and You become compliant prior to 30 days after+Your receipt of the notice.++5.2. If You initiate litigation against any entity by asserting a patent+infringement claim (excluding declaratory judgment actions,+counter-claims, and cross-claims) alleging that a Contributor Version+directly or indirectly infringes any patent, then the rights granted to+You by any and all Contributors for the Covered Software under Section+2.1 of this License shall terminate.++5.3. In the event of termination under Sections 5.1 or 5.2 above, all+end user license agreements (excluding distributors and resellers) which+have been validly granted by You or Your distributors under this License+prior to termination shall survive termination.++************************************************************************+*                                                                      *+*  6. Disclaimer of Warranty                                           *+*  -------------------------                                           *+*                                                                      *+*  Covered Software is provided under this License on an "as is"       *+*  basis, without warranty of any kind, either expressed, implied, or  *+*  statutory, including, without limitation, warranties that the       *+*  Covered Software is free of defects, merchantable, fit for a        *+*  particular purpose or non-infringing. The entire risk as to the     *+*  quality and performance of the Covered Software is with You.        *+*  Should any Covered Software prove defective in any respect, You     *+*  (not any Contributor) assume the cost of any necessary servicing,   *+*  repair, or correction. This disclaimer of warranty constitutes an   *+*  essential part of this License. No use of any Covered Software is   *+*  authorized under this License except under this disclaimer.         *+*                                                                      *+************************************************************************++************************************************************************+*                                                                      *+*  7. Limitation of Liability                                          *+*  --------------------------                                          *+*                                                                      *+*  Under no circumstances and under no legal theory, whether tort      *+*  (including negligence), contract, or otherwise, shall any           *+*  Contributor, or anyone who distributes Covered Software as          *+*  permitted above, be liable to You for any direct, indirect,         *+*  special, incidental, or consequential damages of any character      *+*  including, without limitation, damages for lost profits, loss of    *+*  goodwill, work stoppage, computer failure or malfunction, or any    *+*  and all other commercial damages or losses, even if such party      *+*  shall have been informed of the possibility of such damages. This   *+*  limitation of liability shall not apply to liability for death or   *+*  personal injury resulting from such party's negligence to the       *+*  extent applicable law prohibits such limitation. Some               *+*  jurisdictions do not allow the exclusion or limitation of           *+*  incidental or consequential damages, so this exclusion and          *+*  limitation may not apply to You.                                    *+*                                                                      *+************************************************************************++8. Litigation+-------------++Any litigation relating to this License may be brought only in the+courts of a jurisdiction where the defendant maintains its principal+place of business and such litigation shall be governed by laws of that+jurisdiction, without reference to its conflict-of-law provisions.+Nothing in this Section shall prevent a party's ability to bring+cross-claims or counter-claims.++9. Miscellaneous+----------------++This License represents the complete agreement concerning the subject+matter hereof. If any provision of this License is held to be+unenforceable, such provision shall be reformed only to the extent+necessary to make it enforceable. Any law or regulation which provides+that the language of a contract shall be construed against the drafter+shall not be used to construe this License against a Contributor.++10. Versions of the License+---------------------------++10.1. New Versions++Mozilla Foundation is the license steward. Except as provided in Section+10.3, no one other than the license steward has the right to modify or+publish new versions of this License. Each version will be given a+distinguishing version number.++10.2. Effect of New Versions++You may distribute the Covered Software under the terms of the version+of the License under which You originally received the Covered Software,+or under the terms of any subsequent version published by the license+steward.++10.3. Modified Versions++If you create software not governed by this License, and you want to+create a new license for such software, you may create and use a+modified version of this License if you rename the license and remove+any references to the name of the license steward (except to note that+such modified license differs from this License).++10.4. Distributing Source Code Form that is Incompatible With Secondary+Licenses++If You choose to distribute Source Code Form that is Incompatible With+Secondary Licenses under the terms of this version of the License, the+notice described in Exhibit B of this License must be attached.++Exhibit A - Source Code Form License Notice+-------------------------------------------++  This Source Code Form is subject to the terms of the Mozilla Public+  License, v. 2.0. If a copy of the MPL was not distributed with this+  file, You can obtain one at http://mozilla.org/MPL/2.0/.++If it is not possible or desirable to put the notice in a particular+file, then You may include the notice in a location (such as a LICENSE+file in a relevant directory) where a recipient would be likely to look+for such a notice.++You may add additional accurate notices of copyright ownership.++Exhibit B - "Incompatible With Secondary Licenses" Notice+---------------------------------------------------------++  This Source Code Form is "Incompatible With Secondary Licenses", as+  defined by the Mozilla Public License, v. 2.0.
+ README.md view
@@ -0,0 +1,154 @@+# prolens++![Prolens Logo](https://user-images.githubusercontent.com/8126674/95865685-da91b080-0d5e-11eb-91cd-b6a7bae29262.png)++[![GitHub CI](https://github.com/kowainik/prolens/workflows/CI/badge.svg)](https://github.com/kowainik/prolens/actions)+[![Hackage](https://img.shields.io/hackage/v/prolens.svg?logo=haskell)](https://hackage.haskell.org/package/prolens)+[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](LICENSE)++The `prolens` package is a Haskell library with a __minimal__ and+__lightweight__ implementation of _optics_. __Optic__ is a high-level+concept for values that provide _composable_ access to different parts of+structures.++Prolens implements the following optics:++* __Lens__ — composable getters and setters+* __Prism__ — composable constructors and deconstructors+* __Traversal__ — composable data structures visitors++## Goals++We created the `prolens` project in pursuit of the following goals:++1. __Education__. Teach others how to implement and work with+   profunctor optics. This also means that some underlying types or+   type variables have different unconventional names+2. __Learning__. Explore new concepts ourselves and understand better+   abstractions used in the implementation.+3. __Minimalism__. Keep the number of dependencies, features and code+   low, but still solve common problems.+4. __Performance__. Despite being minimalist, implement optics so they+   are as fast as manual and clumsy pattern matching.+5. __Exploration__. Understand how different modern Haskell features+   can work on improving interface and bring new flavour into standard+   approaches. Because of this, we implement our own `Profunctor`+   typeclass with the+   [QuantifiedConstraints](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/glasgow_exts.html#quantified-constraints)+   feature, which is not present in any other library at the moment.+6. __Profunctors__. We use profunctor encoding of optics because it+   has more elegant design with fewer surprises.++## Features++1. __Lightweight__. Only+   [base](http://hackage.haskell.org/package/base)+   in dependencies. The project itself also has a rather small amount+   of code.+2. __Fast__. Despite being lightweight, `prolens` provides a+   performant API. We use the+   [inspection-testing](https://hackage.haskell.org/package/inspection-testing)+   library to guarantee that our implementation of optics compiles to+   the same code as plain Haskell getters, record-update syntax and+   pattern matching.+3. __Excellent documentation__. The `prolens` library contains a+   mini-tutorial on optics, enough to understand how and when to use+   basic lenses and prisms.+4. __Beginner-friendly__. The abstractions in the implementation are+   hardcore, but our documentation presents the concept in a+   beginner-friendly and approachable manner.+5. __Lawful__. We use property-based testing to make sure that laws of+   all underlying abstractions are verified.++## How to use++`prolens` is compatible with the latest GHC compiler+versions starting from `8.6.5`.++In order to start using `prolens` in your project, you+will need to set it up with the three easy steps:++1. Add the dependency on `prolens` in your project's+   `.cabal` file. For this, you should modify the `build-depends`+   section by adding the name of this library. After the adjustment,+   this section could look like this:++   ```haskell+   build-depends: base ^>= 4.14+                , prolens ^>= 0.0+   ```+2. In the module where you wish to use composable getters and setters,+   you should add the import:++   ```haskell+   import Prolens (Lens', lens, view)+   ```+3. Now you can use the types and functions from the library:++   ```haskell+   data User = User+       { userName :: String+       , userAge  :: Int+       }++   nameL :: Lens' User String+   nameL = lens userName (\u new -> u { userName = new })++   main :: IO ()+   main = putStrln $ view nameL (User "Johnny" 27)+   ```++### Usage with Stack++If `prolens` is not available on your current Stackage+resolver yet, fear not! You can still use it from Hackage by adding+the following to the `extra-deps` section of your `stack.yaml` file:++```yaml+extra-deps:+  - prolens-0.0.0.0+```++## Comparison to other libraries++1. [lens](https://hackage.haskell.org/package/lens)++   It is the most mature Haskell library for optics. `lens` provides a+   richer interface, but it is heavyweight and based on Van Laarhoven (VL)+   encoding of lenses.++2. [microlens](https://hackage.haskell.org/package/microlens)++   A lightweight implementation of optics compatible with+   `lens`. `microlens` is also minimalistic, but it doesn't provide+   prisms and is based on VL encoding.++3. [optics](https://hackage.haskell.org/package/optics)++   The `optics` library uses the profunctor encoding. It provides much+   more features than `prolens`, but at the same time it's+   heavyweight. Also, `optics` uses an opaque representation of optics+   (e.g. they are wrapped in a newtype), which means that they are+   composed using the custom operator `%`, while in `prolens` optics+   are type aliases to functions and can be easily composed with the+   dot `.` operator.++4. [profunctor-optics](https://hackage.haskell.org/package/profunctor-optics)++   This library is also based on profunctor encoding (as the name+   suggests) and provides optics as aliases to functions. But it is+   more heavyweight, though it provides more features.++In addition to this per-library comparison, `prolens` has a few unique+features:++  * Beginner-friendly documentation with usage examples+  * Usage of `inspection-testing` to guarantee the performance of+    optics+  * Property-based tests of lens and typeclasses laws to make sure+    that all abstractions behave properly++## Acknowledgement++  * Edward Kmett for lenses and profunctor typeclasses+  * Well-Typed for the implementation of `optics`
+ prolens.cabal view
@@ -0,0 +1,99 @@+cabal-version:       2.4+name:                prolens+version:             0.0.0.0+synopsis:            Profunctor-based lightweight implementation of optics+description:+    Lightweight and performance implementation of optics — lenses, prisms, traversals.+    .+    The library uses hardcore abstractions internally, but provides+    beginner-friendly, composable and convenient interface for working+    with data structures.+homepage:            https://github.com/kowainik/prolens+bug-reports:         https://github.com/kowainik/prolens/issues+license:             MPL-2.0+license-file:        LICENSE+author:              Veronika Romashkina, Dmitrii Kovanikov+maintainer:          Kowainik <xrom.xkov@gmail.com>+copyright:           2020 Kowainik+category:            Data, Optics, Lenses+build-type:          Simple+extra-doc-files:     README.md+                     CHANGELOG.md+tested-with:         GHC == 8.6.5+                     GHC == 8.8.4+                     GHC == 8.10.2++source-repository head+  type:                git+  location:            https://github.com/kowainik/prolens.git++common common-options+  build-depends:       base >= 4.12.0.0 && < 4.15++  ghc-options:         -Wall+                       -Wcompat+                       -Widentities+                       -Wincomplete-uni-patterns+                       -Wincomplete-record-updates+                       -Wredundant-constraints+  if impl(ghc >= 8.2)+    ghc-options:       -fhide-source-paths+  if impl(ghc >= 8.4)+    ghc-options:       -Wmissing-export-lists+                       -Wpartial-fields+  if impl(ghc >= 8.8)+    ghc-options:       -Wmissing-deriving-strategies+                       -fwrite-ide-info+                       -hiedir=.hie+  if impl(ghc >= 8.10)+    ghc-options:       -Wunused-packages++  default-language:    Haskell2010+  default-extensions:  ConstraintKinds+                       DeriveGeneric+                       DerivingStrategies+                       GeneralizedNewtypeDeriving+                       InstanceSigs+                       KindSignatures+                       LambdaCase+                       OverloadedStrings+                       RecordWildCards+                       ScopedTypeVariables+                       StandaloneDeriving+                       TupleSections+                       TypeApplications+                       ViewPatterns++common common-test+  import:              common-options+  hs-source-dirs:      test+  ghc-options:         -threaded++library+  import:              common-options+  hs-source-dirs:      src+  exposed-modules:     Prolens++test-suite prolens-test+  import:              common-test+  type:                exitcode-stdio-1.0+  main-is:             Spec.hs++  other-modules:       Test.Data+                       Test.Prolens+                       Test.Prolens.Property+                       Test.Prolens.Inspection++  build-depends:       prolens+                     , hedgehog  >= 1.0.2 && < 2+                     , hspec ^>= 2.7.4+                     , hspec-hedgehog+                     , inspection-testing ^>= 0.4+  ghc-options:         -rtsopts+                       -with-rtsopts=-N++test-suite doctest+  import:              common-test+  type:                exitcode-stdio-1.0+  main-is:             Doctest.hs+  build-depends:       doctest ^>= 0.17
+ src/Prolens.hs view
@@ -0,0 +1,989 @@+{-# OPTIONS_GHC -Wno-redundant-constraints #-}++{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE TypeFamilies          #-}++{- |+Copyright: (c) 2020 Kowainik+SPDX-License-Identifier: MPL-2.0+Maintainer: Kowainik <xrom.xkov@gmail.com>++The @prolens@ package is a Haskell library with a minimal and lightweight+implementation of optics based on 'Profunctor's. __'Optic'__ is a high-level+concept for values that provide composable access to different parts of structures.++"Prolens" implements the following optics:++ * 'Lens' — composable getters and setters+ * 'Prism' — composable constructors and deconstructors+ * 'Traversal' — composable data structures visitors++== Usage++To use lenses or prisms in your project, you need to add @prolens@ package as+the dependency in the @build-depends@ field of your @.cabal@ file. E.g.:++@+build-depends: prolens ^>= 0.0.0.0+@++You should add the import of this module in the place of lenses usage:++@+__import__ "Prolens"+@++== Creating your own optics++We show in each section of this module how to create values of each+kind of optics.++⚠️ __The general crucial rule__ for achieving maximum performance:+always add @\{\-\# INLINE ... \#\-\}@ pragmas to your optics.++== Typeclasses table++The below table shows required constraints for each 'Optic':+++-------------+------------------------------++| Optic       | Constraints                  |++=============+==============================++| 'Lens'      | @'Strong' p@                 |++-------------+------------------------------++| 'Prism'     | @'Choice' p@                 |++-------------+------------------------------++| 'Traversal' | @('Choice' p, 'Monoidal' p)@ |++-------------+------------------------------+++== Usage table: get, set, modify++Here is a go-to table on how to use getter, setters and modifiers with different+'Optic's.+++-------------+------------------+--------------+------------------+------------------+-----------------+-----------------++|             | get              | get operator | set              | set operator     | modify          | modify operator |++=============+==================+==============+==================+==================+=================+=================++| 'Lens'      | @'view' l x@     | @x '^.' l@   | @'set' l new x@  | @x & l '.~' new@ | @'over' l f x@  | @x & l '%~' f@  |++-------------+------------------+--------------+------------------+------------------+-----------------+-----------------++| 'Prism'     | @'preview' _L x@ | -            | @'set' _L new x@ | -                | @'over' _L f x@ | -               |++-------------+------------------+--------------+------------------+------------------+-----------------+-----------------++| 'Traversal' | @'view' l x@     | -            | @'set' l new x@  | -                | @'over' l f x@  | -               |++-------------+------------------+--------------+------------------+------------------+-----------------+-----------------+++@since 0.0.0.0+-}++module Prolens+    ( -- * Profunctor typeclass+      Profunctor (..)++      -- * Optics+    , Optic++      -- * Lenses+      -- $lenses++      -- ** Lenses types+    , Lens+    , Lens'+      -- ** Strong typeclass+    , Strong (..)++      -- ** Lenses functions+    , set+    , over+    , view+    , lens++      -- ** Lenses operators+    , (^.)+    , (.~)+    , (%~)++      -- ** Standard lenses+    , fstL+    , sndL++      -- * Prisms+      -- $prisms++      -- ** Prism types+    , Prism+    , Prism'+      -- ** Choice typeclass+    , Choice (..)++      -- ** Prism functions+    , prism+    , prism'+    , preview++      -- ** Standard Prisms+    , _Just+    , _Left+    , _Right++      -- * Traversals++      -- ** Traversal types+    , Traversal+      -- ** Monoidal typeclass+    , Monoidal (..)++      -- ** Traversal functions+    , traverseOf++      -- ** Standard traversals+    , eachPair+    , eachMaybe+    , eachList++      -- * Internal data types+    , Forget (..)+    , Fun (..)+    ) where++import Control.Applicative (Const (..), liftA2)+import Data.Coerce (coerce)+import Data.Monoid (First (..))++-- $setup+-- >>> import Data.Function ((&))+++{- | The type @p@ is called 'Profunctor' and it means, that a value of+type @p in out@ takes a value of type @in@ as an argument (input) and+outputs a value of type @out@. 'Profunctor' allows mapping of inputs+and outputs.++@+          +----> Result input+          |+          |                                +--> Original profunctor+          |      +-> Original input        |+          +      +                         ++dimap :: (in2 -> in1) -> (out1 -> out2) -> p in1 out1 -> p in2 out2+                          +       ++                          |       +-> Result output+                          |+                          +-> Original output+@++Speaking in terms of other abstractions, 'Profunctor' is+'Data.Functor.Contravariant.Contravariant' in the first type argument+(type variable @in@) and 'Functor' in the second type argument (type+variable @out@).++Moreover, @p in@ must have 'Functor' instance first to implement the+'Profunctor' instance. This required using @QuantifiedConstraints@.++@+                         Contravariant <---++                                           |+                                         +-+-++                                         +   ++(forall a . Functor (p a)) => Profunctor p a b+          +                              + ++          |                              | |+          +--> Quantified constraint     ++++                                          |+                              Functor  <--++@++Instances of 'Profunctor' should satisfy the following laws:++* __Identity:__ @'dimap' 'id' 'id' ≡ 'id'@+* __Composition:__ @'dimap' (inAB . inBC) (outYZ . outXY) ≡ 'dimap' outBC outYZ . 'dimap' outAB outXY@++@since 0.0.0.0+-}+-- type Profunctor :: (Type -> Type -> Type) -> Constraint+class (forall a . Functor (p a)) => Profunctor p where+    dimap+        :: (in2 -> in1)  -- ^ Map input+        -> (out1 -> out2)  -- ^ Map output+        -> p in1 out1  -- ^ Take @in1@ as input and return @out1@+        -> p in2 out2  -- ^ Take @in2@ as input and return @out2@++-- | @since 0.0.0.0+instance Profunctor (->) where+    dimap :: (in2 -> in1) -> (out1 -> out2) -> (in1 -> out1) -> (in2 -> out2)+    dimap in21 out12 f = out12 . f . in21+    {-# INLINE dimap #-}++{- | @'Fun' m a b@ is a wrapper for function @a -> m b@.++@since 0.0.0.0+-}+newtype Fun m a b = Fun+    { unFun :: a -> m b+    }++-- | @since 0.0.0.0+instance Functor m => Functor (Fun m x) where+    fmap :: (a -> b) -> Fun m x a -> Fun m x b+    fmap f (Fun xma) = Fun (fmap f . xma)+    {-# INLINE fmap #-}++-- | @since 0.0.0.0+instance Functor m => Profunctor (Fun m) where+    dimap :: (in2 -> in1) -> (out1 -> out2) -> Fun m in1 out1 -> Fun m in2 out2+    dimap in21 out12 (Fun f) = Fun (fmap out12 . f . in21)+    {-# INLINE dimap #-}++{- | 'Strong' is a 'Profunctor' that can be lifted to take a pair as+an input and return a pair.++The second element of a pair (variable of type @c@) can be of any+type, and you can decide what type it should be. This is convenient+for implementing various functions. E.g. 'lens' uses this fact.++@since 0.0.0.0+-}+class Profunctor p => Strong p where+    first  :: p a b -> p (a, c) (b, c)+    second :: p a b -> p (c, a) (c, b)++-- | @since 0.0.0.0+instance Strong (->) where+    first :: (a -> b) -> (a, c) -> (b, c)+    first ab (a, c) = (ab a, c)+    {-# INLINE first #-}++    second :: (a -> b) -> (c, a) -> (c, b)+    second ab (c, a) = (c, ab a)+    {-# INLINE second #-}++-- | @since 0.0.0.0+instance (Functor m) => Strong (Fun m) where+    first :: Fun m a b -> Fun m (a, c) (b, c)+    first (Fun amb) = Fun (\(a, c) -> fmap (, c) (amb a))+    {-# INLINE first #-}++    second :: Fun m a b -> Fun m (c, a) (c, b)+    second (Fun amb) = Fun (\(c, a) -> fmap (c,) (amb a))+    {-# INLINE second #-}++{- | 'Choice' is a 'Profunctor' that can be lifted to work with+'Either' given input or some other value.++The other element of 'Either' (variable of type @c@) can be of any+type, and you can decide what type it should be. This is convenient+for implementing various functions. E.g. 'prism' uses this fact.++@since 0.0.0.0+-}+class Profunctor p => Choice p where+    left  :: p a b -> p (Either a c) (Either b c)+    right :: p a b -> p (Either c a) (Either c b)++-- | @since 0.0.0.0+instance Choice (->) where+    left  :: (a -> b) -> Either a c -> Either b c+    left ab = \case+        Left a  -> Left $ ab a+        Right c -> Right c+    {-# INLINE left #-}++    right :: (a -> b) -> Either c a -> Either c b+    right ab = \case+        Right a -> Right $ ab a+        Left c  -> Left c+    {-# INLINE right #-}++-- | @since 0.0.0.0+instance (Applicative m) => Choice (Fun m) where+    left :: Fun m a b -> Fun m (Either a c) (Either b c)+    left (Fun amb)= Fun $ \eitherAc -> case eitherAc of+        Left a  -> Left <$> amb a+        Right c -> pure $ Right c+    {-# INLINE left #-}++    right :: Fun m a b -> Fun m (Either c a) (Either c b)+    right (Fun amb)= Fun $ \eitherCa -> case eitherCa of+        Right a -> Right <$> amb a+        Left c  -> pure $ Left c+    {-# INLINE right #-}++{- | 'Monoidal' is 'Strong' 'Profunctor' that can be appended. It is+similar to 'Monoid's but for higher-kinded types.++@since 0.0.0.0+-}+class Strong p => Monoidal p where+    pappend :: p a b -> p c d -> p (a, c) (b, d)+    pempty :: p a a++-- | @since 0.0.0.0+instance Monoidal (->) where+    pappend :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)+    pappend ab cd (a, c) = (ab a, cd c)+    {-# INLINE pappend #-}++    pempty :: a -> a+    pempty = id+    {-# INLINE pempty #-}++-- | @since 0.0.0.0+instance (Applicative m) => Monoidal (Fun m) where+    pappend :: Fun m a b -> Fun m c d -> Fun m (a, c) (b, d)+    pappend (Fun amb) (Fun cmd) = Fun (\(a, c) -> liftA2 (,) (amb a) (cmd c))+    {-# INLINE pappend #-}++    pempty :: Fun m a a+    pempty = Fun (pure . id)+    {-# INLINE pempty #-}++{- | 'Optic' takes a connection from @a@ to @b@ (represented as a+value of type @p a b@) and returns a connection from @source@ to+@target@ (represented as a value of type @p source target@).++@+           +---> Profunctor+           |+           | +----> Final input+           | |+           | |      +-> Final output+           | |      |+           + +      ++type Optic p source target a b+                           + ++                           | |+            Given input <--+ |+                             |+        Given output <-------++@++@since 0.0.0.0+-}+type Optic p source target a b = p a b -> p source target+++{- $lenses++== Example++To understand better how to use this library lets look at some simple example.+Let's say we have the user and address data types in our system:++>>> :{+data Address = Address+    { addressCountry :: String+    , addressCity    :: String+    , addressIndex   :: String+    } deriving (Show)+:}++>>> :{+data User = User+    { userName    :: String+    , userAge     :: Int+    , userAddress :: Address+    } deriving (Show)+:}++We can easily get fields of the @User@ and @Address@ types, but+setting values is inconvenient (especially for nested records). To+solve this problem, we can use lenses — composable getters and+setters. 'Lens' is a value, so we need to define lenses for fields of+our data types first.++To create the lens for the @userName@ field we can use 'lens' function and+manually writing getter and setter function:++>>> :{+nameL :: Lens' User String+nameL = lens getter setter+  where+    getter :: User -> String+    getter = userName+    setter :: User -> String -> User+    setter user newName = user {userName = newName}+:}++In this manner, we can create other lenses for our @User@ data type.+Usually, lenses are one-liners, and we can define them easily using lambda-functions.++>>> :{+ageL :: Lens' User Int+ageL = lens userAge (\u new -> u {userAge = new})+:}++>>> :{+addressL :: Lens' User Address+addressL = lens userAddress (\u new -> u {userAddress = new})+:}++We want to have lenses for accessing @Adress@ fields inside @User@, so we want to have the following values:++@+countryL :: 'Lens'' User 'String'+cityL    :: 'Lens'' User 'String'+indexL   :: 'Lens'' User 'String'+@++/Note:/ for lenses as @countryL@, @cityL@ etc., we are using composition of the+lenses for the @userAddress@ field. If we have++>>> :{+addressCityL :: Lens' Address String+addressCityL = lens addressCity (\a new -> a {addressCity = new})+:}++then++>>> cityL = addressL . addressCityL++Let's say we have some sample user++>>> :{+address = Address+    { addressCountry = "UK"+    , addressCity    = "London"+    , addressIndex   = "XXX"+    }+user :: User+user = User+    { userName = "John"+    , userAge  = 42+    , userAddress = address+    }+:}++To view the fields of the User data type we can use 'view' or '^.'++>>> view ageL user+42+>>> user ^. cityL+"London"++If we want to change any of the user's data, we should use 'set' or '.~'++>>> set nameL "Johnny" user+User {userName = "Johnny", userAge = 42, userAddress = Address {addressCountry = "UK", addressCity = "London", addressIndex = "XXX"}}+>>> user & cityL .~ "Bristol"+User {userName = "John", userAge = 42, userAddress = Address {addressCountry = "UK", addressCity = "Bristol", addressIndex = "XXX"}}++'over' or '%~' operator could be useful when, for example, you want to increase the age by one on the user's birthday:++>>> over ageL succ user+User {userName = "John", userAge = 43, userAddress = Address {addressCountry = "UK", addressCity = "London", addressIndex = "XXX"}}+>>> user & ageL %~ succ+User {userName = "John", userAge = 43, userAddress = Address {addressCountry = "UK", addressCity = "London", addressIndex = "XXX"}}+-}++{- | 'Lens' represents composable getters and setters.++'Lens' is an @'Optic' p@ with the 'Strong' constraint on the @p@ type variable.++@+          +---> Current object+          |+          |      +-> Final object+          |      |+          +      ++type Lens source target a b+                        + ++                        | |+       Current field <--+ |+                          |+      Final field <-------++@++@since 0.0.0.0+-}+type Lens source target a b = forall p . Strong p => Optic p source target a b++{- | The monomorphic lenses which don't change the type of the container (or of+the value inside). It has a 'Strong' constraint, and it can be used whenever a+getter or a setter is needed.++  * @a@ is the type of the value inside of structure+  * @source@ is the type of the whole structure++For most use-cases it's enought to use this 'Lens'' instead of more general 'Lens'.++@since 0.0.0.0+-}+type Lens' source a = Lens source source a a++{- | Sets the given value to the structure using a setter.++@since 0.0.0.0+-}+set :: (p ~ (->))+    => Optic p source target a b  -- ^ 'Optic' that can be lens+    -> b  -- ^ Value to set+    -> source  -- ^ Object where we want to set value+    -> target  -- ^ Resulting object with @b@ set+set abst = abst . const+{-# INLINE set #-}++{- | Applies the given function to the target.++@since 0.0.0.0+-}+over+    :: (p ~ (->))+    => Optic p source target a b  -- ^ 'Optic' that can be lens+    -> (a -> b)  -- ^ Field modification function+    -> source  -- ^ Object where we want to set value+    -> target  -- ^ Resulting object with the modified field+over = id+{-# INLINE over #-}++{- | Gets a value out of a structure using a getter.++@since 0.0.0.0+-}+view+    :: (p ~ Fun (Const a))+    => Optic p source target a b  -- ^ 'Optic' that can be lens+    -> source  -- ^ Object from which we want to get value+    -> a  -- ^ Field of @source@+view opt = coerce (opt (Fun Const))+{-# INLINE view #-}+-- view opt = getConst . unFun (opt (Fun Const))+-- opt :: Fun (Const a) a b -> Fun (Const a) s t+-- opt :: (a -> Const a b) -> ( s -> Const a t)++{- | Creates 'Lens' from the getter and setter.++@since 0.0.0.0+-}+lens+    :: (source -> a)  -- ^ Getter+    -> (source -> b -> target)  -- ^ Setter+    -> Lens source target a b+lens getter setter = dimap (\s -> (s, getter s)) (uncurry setter) . second+{-# INLINE lens #-}++{- | The operator form of 'view' with the arguments flipped.++@since 0.0.0.0+-}+infixl 8 ^.+(^.) :: source -> Lens' source a -> a+s ^. l = view l s+{-# INLINE (^.) #-}++{- | The operator form of 'set'.++@since 0.0.0.0+-}+infixr 4 .~+(.~) :: Lens' source a -> a -> source -> source+(.~) = set+{-# INLINE (.~) #-}++{- | The operator form of 'over'.++@since 0.0.0.0+-}+infixr 4 %~+(%~) :: Lens' source a -> (a -> a) -> source -> source+(%~) = over+{-# INLINE (%~) #-}++{- | 'Lens'' for a tuple on the first argument.++>>> view fstL (42, "str")+42++@since 0.0.0.0+-}+fstL :: Lens (a, c) (b, c) a b+fstL = lens fst $ \(_, b) new -> (new, b)+{-# INLINE fstL #-}++{- | 'Lens'' for a tuple on the second argument.++>>> view sndL (42, "Hello")+"Hello"++@since 0.0.0.0+-}+sndL :: Lens (x, a) (x, b) a b+sndL = lens snd $ \(a, _) new -> (a, new)+{-# INLINE sndL #-}++{- $prisms+Prisms work with sum types.++== Example++Let's say we have the user data type in our system:++>>> :{+data Address = Address+    { addressCountry :: String+    , addressCity    :: String+    } deriving (Show)+:}++>>> :{+data Payload+    = NamePayload String+    | IdPayload Int+    | AddressPayload Address+    deriving (Show)+:}++To create the prism for each constructor we can use 'prism'' function and+manually writing getter and setter function:++/NOTE:/ The naming convention for prisms is the following:++@+_ConstructorName+@++>>> :{+_NamePayload :: Prism' Payload String+_NamePayload = prism' construct match+  where+    match :: Payload -> Maybe String+    match p = case p of+        NamePayload name -> Just name+        _otherPayload -> Nothing+    construct :: String -> Payload+    construct = NamePayload+:}++In this manner, we can create other prisms for our @Payload@ data type.++>>> :{+_IdPayload :: Prism' Payload Int+_IdPayload = prism' IdPayload $ \p -> case p of+    IdPayload i -> Just i+    _otherPayload -> Nothing+:}++>>> :{+_AddressPayload :: Prism' Payload Address+_AddressPayload = prism' AddressPayload $ \p -> case p of+    AddressPayload a -> Just a+    _otherPayload -> Nothing+:}++Let's say we have some sample payload++>>> :{+payloadName :: Payload+payloadName = NamePayload "Some name"+:}++To view the fields of the @Payload@ data type we can use 'preview'++>>> preview _NamePayload payloadName+Just "Some name"+>>> preview _IdPayload payloadName+Nothing++If we want to change any of the data, we should use 'set' or '.~' (just like in lenses)++>>> set _NamePayload "Johnny" payloadName+NamePayload "Johnny"+>>> set _IdPayload 3 payloadName+NamePayload "Some name"++Note, that you can easily compose lenses and prisms together:++>>> :{+address = Address+    { addressCountry = "UK"+    , addressCity    = "London"+    }+:}++>>> :{+addressCityL :: Lens' Address String+addressCityL = lens addressCity (\a new -> a {addressCity = new})+:}++>>> :{+payloadAddress :: Payload+payloadAddress = AddressPayload address+:}++>>> set _AddressPayload (address & addressCityL .~ "Bristol") payloadAddress+AddressPayload (Address {addressCountry = "UK", addressCity = "Bristol"})+-}++{- | 'Prism' represents composable constructors and deconstructors.++'Prism' is an @'Optic' p@ with 'Choice' constraint on the @p@ type+variable.++@+                   +---> Current object+                   |+                   |      +-> Final object+                   |      |+                   +      ++        type Prism source target a b+                                 + ++                                 | |+ Field in current constructor <--+ |+                                   |+Field in final constructor <-------++@++@since 0.0.0.0+-}+type Prism source target a b = forall p . Choice p => Optic p source target a b++{- | The monomorphic prisms which don't change the type of the container (or of+the value inside).++  * @a@ is the value inside the particular constructor+  * @source@ is some sum type++@since 0.0.0.0+-}+type Prism' source a = Prism source source a a++{- | Newtype around function @a -> r@. It's called /forget/ because it+forgets about its last type variable.++@since 0.0.0.0+-}+newtype Forget r a b = Forget+    { unForget :: a -> r+    }++-- | @since 0.0.0.0+instance Functor (Forget r x) where+    fmap :: (a -> b) -> Forget r x a -> Forget r x b+    fmap _ = coerce++-- | @since 0.0.0.0+instance Profunctor (Forget r) where+    dimap :: (a -> b) -> (c -> d) -> Forget r b c -> Forget r a d+    dimap ab _cd (Forget br) = Forget (br . ab)+    {-# INLINE dimap #-}++-- | @since 0.0.0.0+instance Strong (Forget r) where+    first :: Forget r a b -> Forget r (a, c) (b, c)+    first (Forget ar) = Forget (ar . fst)+    {-# INLINE first #-}++    second :: Forget r a b -> Forget r (c, a) (c, b)+    second (Forget ar) = Forget (ar . snd)+    {-# INLINE second #-}++-- | @since 0.0.0.0+instance Monoid r => Choice (Forget r) where+    left :: Forget r a b -> Forget r (Either a c) (Either b c)+    left (Forget ar) = Forget (either ar (const mempty))+    {-# INLINE left #-}++    right :: Forget r a b -> Forget r (Either c a) (Either c b)+    right (Forget ar) = Forget (either (const mempty) ar)+    {-# INLINE right #-}++-- | @since 0.0.0.0+instance (Monoid r) => Monoidal (Forget r) where+    pappend :: Forget r a b -> Forget r c d -> Forget r (a, c) (b, d)+    pappend (Forget ar) (Forget cr) = Forget (\(a, c) -> ar a <> cr c)+    {-# INLINE pappend #-}++    pempty :: Forget r a a+    pempty = Forget (const mempty)+    {-# INLINE pempty #-}++{- | Match a value from @source@ type.++@since 0.0.0.0+-}+preview+    :: forall a source p+    .  (p ~ Forget (First a))+    => Optic p source source a a  -- ^ 'Optic' that can be prism+    -> source  -- ^ Object (possible sum type)+    -> Maybe a  -- ^ Value of type @a@ from a specific constructor+preview paapss = coerce (paapss wrap)+  where+    wrap :: Forget (First a) a a+    wrap = coerce @(a -> Maybe a) @(Forget (First a) a a) Just+    {-# INLINE wrap #-}+{-# INLINE preview #-}+-- preview paapss = getFirst . unForget (paapss (Forget (First . Just)))+-- paapss :: Forget (First a) a a -> Forget (First a) source source+-- paapss :: (a -> First a) -> source -> First a+-- paapss :: (a -> Maybe a) -> source -> Maybe a++{- | Create 'Prism' from constructor and matching function.++@since 0.0.0.0+-}+prism+    :: (b -> target)  -- ^ Constructor+    -> (source -> Either target a)  -- ^ Matching function+    -> Prism source target a b+-- prism :: (b -> target) -> (source -> Either target a) -> p a b -> p source target+prism ctor match = dimap match (either id ctor) . right+{-# INLINE prism #-}++{- | Create monomorphic 'Prism'' from constructor and matching function.++@since 0.0.0.0+-}+prism'+    :: (a -> source)  -- ^ Constructor+    -> (source -> Maybe a)  -- ^ Matching function+    -> Prism' source a+prism' ctor match = prism ctor (\s -> maybe (Left s) Right $ match s)+{-# INLINE prism' #-}++{- | 'Prism' for a 'Just' of 'Maybe'.++>>> preview _Just (Just 42)+Just 42++>>> preview _Just Nothing+Nothing++@since 0.0.0.0+-}+_Just :: Prism (Maybe a) (Maybe b) a b+_Just = prism Just $ \case+    Just a  -> Right a+    Nothing -> Left Nothing+{-# INLINE _Just #-}+++{- | 'Prism' for a 'Left' of 'Either'.++>>> preview _Left (Left 42)+Just 42++>>> preview _Left (Right "Hello")+Nothing++@since 0.0.0.0+-}+_Left :: Prism (Either a x) (Either b x) a b+_Left = prism Left $ \case+    Left l  -> Right l+    Right r -> Left $ Right r+{-# INLINE _Left #-}++{- | 'Prism' for a 'Left' of 'Either'.++>>> preview _Right (Left 42)+Nothing++>>> preview _Right (Right "Hello")+Just "Hello"++@since 0.0.0.0+-}+_Right :: Prism (Either x a) (Either x b) a b+_Right = prism Right $ \case+    Right a -> Right a+    Left x  -> Left $ Left x+{-# INLINE _Right #-}+++{- | 'Traversal' provides composable ways to visit different parts of+a data structure.++'Traversal' is an @'Optic' p@ with the 'Choice' and 'Monoidal'+constraints on the @p@ type variable.++@+               +---> Current collection+               |+               |      +-> Final collection+               |      |+               +      ++type Traversal source target a b+                             + ++                             | |+          Current element <--+ |+                               |+         Final element <-------++@++@since 0.0.0.0+-}+type Traversal source target a b+    = forall p+    . (Choice p, Monoidal p)+    => Optic p source target a b++{- | Traverse a data structure using given 'Traversal'.++>>> traverseOf eachPair putStrLn ("Hello", "World!")+Hello+World!+((),())++@since 0.0.0.0+-}+traverseOf+    :: (Applicative f, p ~ Fun f)+    => Optic p source target a b  -- ^ 'Optic' that can be a traversal+    -> (a -> f b)  -- ^ Traversing function+    -> source  -- ^ Data structure to traverse+    -> f target  -- ^ Traversing result+traverseOf pabPst aFb = unFun (pabPst (Fun aFb))+-- pabPst :: Fun f a b -> Fun f source target+-- pabPst :: (a -> f b) -> Fun f source target++{- | 'Traversal' for a pair of same type elements.++>>> over eachPair (+ 1) (3, 7)+(4,8)++@since 0.0.0.0+-}+eachPair :: Traversal (a, a) (b, b) a b+eachPair pab = pappend pab pab++{- | 'Traversal' for a 'Maybe'.++>>> over eachMaybe (+ 1) (Just 3)+Just 4+>>> over eachMaybe (+ 1) Nothing+Nothing++@since 0.0.0.0+-}+eachMaybe :: Traversal (Maybe a) (Maybe b) a b+eachMaybe pab = dimap maybeToEither eitherToMaybe (left pab)+  where+    maybeToEither :: Maybe a -> Either a ()+    maybeToEither = \case+        Just a  -> Left a+        Nothing -> Right ()++    eitherToMaybe :: Either a () -> Maybe a+    eitherToMaybe = \case+        Left a   -> Just a+        Right () -> Nothing++{- | 'Traversal' for lists.++>>> over eachList (+ 1) [1..5]+[2,3,4,5,6]+>>> over eachList (+ 1) []+[]++@since 0.0.0.0+-}+eachList :: Traversal [a] [b] a b+eachList pab = dimap listToEither eitherToList $ left $ pappend pab (eachList pab)+  where+    listToEither :: [a] -> Either (a, [a]) ()+    listToEither = \case+        []   -> Right ()+        x:xs -> Left (x, xs)++    eitherToList :: Either (a, [a]) () -> [a]+    eitherToList = \case+        Right ()     -> []+        Left (x, xs) -> x:xs
+ test/Doctest.hs view
@@ -0,0 +1,15 @@+module Main (main) where++import Test.DocTest (doctest)+++main :: IO ()+main = doctest+    $ "-XLambdaCase"+    : "-XInstanceSigs"+    : "-XScopedTypeVariables"+    : "-XTupleSections"+    : "-XTypeApplications"+    :+    [ "src/Prolens.hs"+    ]
+ test/Spec.hs view
@@ -0,0 +1,15 @@+module Main (main) where++import Test.Hspec (hspec)++import Test.Prolens (unitSpecs)+import Test.Prolens.Inspection (inspectionSpecs)+import Test.Prolens.Property (lensPropertySpecs, typeclassesPropertySpecs)+++main :: IO ()+main = hspec $ do+    unitSpecs+    inspectionSpecs+    lensPropertySpecs+    typeclassesPropertySpecs
+ test/Test/Data.hs view
@@ -0,0 +1,131 @@+module Test.Data+    ( -- * Lenses+      Haskeller (..)+    , nameL+    , knowledgeL++    , Knowledge (..)+    , syntaxL++    , me++      -- * Prisms+    , Grade (..)+    , _Mark++    , gradeMark+    , gradeOther++      -- * Generators+    , genFun+    , genFunction+    , genHaskeller+    , genInt+    , genKnowledge+    , genName+    ) where++import Test.Hspec.Hedgehog (MonadGen)++import Prolens (Fun (..), Lens', Prism', lens, prism')++import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range+++data Haskeller = Haskeller+    { haskellerName       :: String+    , haskellerExperience :: Int+    , haskellerKnowledge  :: Knowledge+    } deriving stock (Show, Eq)++data Knowledge = Knowledge+    { kSyntax         :: Bool+    , kMonads         :: Bool+    , kLens           :: Bool+    , kTypeLevelMagic :: Bool+    , kNix            :: Bool+    } deriving stock (Show, Eq)++me :: Haskeller+me = Haskeller+    { haskellerName = "Veronika"+    , haskellerExperience = 2+    , haskellerKnowledge = Knowledge+        { kSyntax = True+        , kMonads = True+        , kLens = True+        , kTypeLevelMagic = True+        , kNix = False+        }+    }++nameL :: Lens' Haskeller String+nameL = lens haskellerName (\h new -> h { haskellerName = new })+{-# INLINE nameL #-}++knowledgeL :: Lens' Haskeller Knowledge+knowledgeL = lens haskellerKnowledge (\h new -> h { haskellerKnowledge = new })+{-# INLINE knowledgeL #-}++syntaxL :: Lens' Knowledge Bool+syntaxL = lens kSyntax (\k new -> k { kSyntax = new })+{-# INLINE syntaxL #-}++data Grade+    = Mark Int+    | Other String+    deriving stock (Show, Eq)++_Mark :: Prism' Grade Int+_Mark = prism' Mark $ \case+    Mark a -> Just a+    _other -> Nothing+{-# INLINE _Mark #-}++gradeMark :: Grade+gradeMark = Mark 5++gradeOther :: Grade+gradeOther = Other "Satisfactory"++-- Generators++genKnowledge :: (MonadGen m) => m Knowledge+genKnowledge = do+    kSyntax <- Gen.bool+    kMonads <- Gen.bool+    kLens <- Gen.bool+    kTypeLevelMagic <- Gen.bool+    kNix <- Gen.bool+    pure Knowledge{..}++genHaskeller :: (MonadGen m) => m Haskeller+genHaskeller = do+    haskellerName <- genName+    haskellerExperience <- Gen.int $ Range.linear 0 50+    haskellerKnowledge <- genKnowledge+    pure Haskeller{..}++genName :: MonadGen m => m String+genName = Gen.string (Range.linear 1 50) Gen.alphaNum++genInt :: MonadGen m => m Int+genInt = Gen.enumBounded++genFunction :: MonadGen m => m (Int -> Int)+genFunction = genInt >>= \n -> Gen.element+    [ id+    , const n+    , (+ n)+    , (* n)+    , subtract n+    , \x -> if x >= n then 1 else 0+    ]++genFun :: MonadGen m => m (Fun Maybe Int Int)+genFun = genFunction >>= \f -> Gen.element $ map Fun+    [ Just+    , const Nothing+    , Just . f+    ]
+ test/Test/Prolens.hs view
@@ -0,0 +1,43 @@+module Test.Prolens+    ( unitSpecs+    ) where++import Data.Function ((&))+import Test.Hspec (Spec, describe, it, shouldBe)++import Prolens (preview, set, view, (.~), (^.))+import Test.Data (Grade (..), gradeMark, gradeOther, me, nameL, _Mark)+++unitSpecs :: Spec+unitSpecs = describe "Prolens unit tests" $ do+    lensSpecs+    prismSpecs+++lensSpecs :: Spec+lensSpecs = describe "Lenses" $ do+    describe "getter" $ do+        it "should get name" $+            view nameL me `shouldBe` "Veronika"+        it "should get name with ^." $+            (me ^. nameL) `shouldBe` "Veronika"+    describe "setter" $ do+        it "should set name" $+            view nameL (set nameL "Dmitrii" me) `shouldBe` "Dmitrii"+        it "should set name with .~" $+            (me & nameL .~ "Dmitrii") ^. nameL `shouldBe` "Dmitrii"+++prismSpecs :: Spec+prismSpecs = describe "Prisms" $ do+    describe "preview" $ do+        it "should get mark" $+            preview _Mark gradeMark `shouldBe` Just 5+        it "should not get mark" $+            preview _Mark gradeOther `shouldBe` Nothing+    describe "set" $ do+        it "should get mark" $+            set _Mark 4 gradeMark `shouldBe` Mark 4+        it "should not get mark" $+            set _Mark 4 gradeOther `shouldBe` gradeOther
+ test/Test/Prolens/Inspection.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE TemplateHaskell #-}++{- | Performance tests for @prolens@. Uses the @inspection-testing@+library to make sure that lenses are as efficient as manual record+getters and update syntax.+-}++module Test.Prolens.Inspection+    ( inspectionSpecs+    ) where++import Test.Hspec (Spec, describe, it, shouldSatisfy, xit)+import Test.Inspection (Result (..), hasNoTypeClasses, inspectTest, (===))++import Prolens (preview, set, view)+import Test.Data (Grade (..), Haskeller (..), Knowledge (..), _Mark, knowledgeL, nameL, syntaxL)+++setNameViaLens :: Haskeller -> Haskeller+setNameViaLens = set nameL "Dmitrii"++setNameManually :: Haskeller -> Haskeller+setNameManually h = h { haskellerName = "Dmitrii" }++getNameViaLens :: Haskeller -> String+getNameViaLens = view nameL++getNameManually :: Haskeller -> String+getNameManually (Haskeller name _ _) = name++setSyntaxViaLens :: Haskeller -> Haskeller+setSyntaxViaLens = set (knowledgeL . syntaxL) True++setSyntaxManually :: Haskeller -> Haskeller+setSyntaxManually h = h { haskellerKnowledge = (haskellerKnowledge h) { kSyntax = True } }++getSyntaxViaLens :: Haskeller -> Bool+getSyntaxViaLens = view (knowledgeL . syntaxL)++getSyntaxManually :: Haskeller -> Bool+getSyntaxManually (Haskeller _ _ (Knowledge syntax _ _ _ _)) = syntax++inspectionSpecs :: Spec+inspectionSpecs = describe "Performance Inspection Testing" $ do+    lensSpecs+    prismSpecs++lensSpecs :: Spec+lensSpecs = describe "Lens" $ do+    describe "set" $ do+        it "setting single via lens is efficient as manual record update" $+            $(inspectTest $ 'setNameViaLens === 'setNameManually) `shouldSatisfy` isSuccess+        it "setting single via lens doesn't have intermediate typeclasses" $+            $(inspectTest $ hasNoTypeClasses 'setNameViaLens) `shouldSatisfy` isSuccess+        it "setting composition via lens is efficient as manual record update" $+            $(inspectTest $ 'setSyntaxViaLens === 'setSyntaxManually) `shouldSatisfy` isSuccess+        it "setting composition via lens doesn't have intermediate typeclasses" $+            $(inspectTest $ hasNoTypeClasses 'setSyntaxViaLens) `shouldSatisfy` isSuccess+    describe "view" $ do+        it "getting single via lens is efficient as plain record function" $+            $(inspectTest $ 'getNameViaLens === 'getNameManually) `shouldSatisfy` isSuccess+        it "getting single via lens doesn't have intermediate typeclasses" $+            $(inspectTest $ hasNoTypeClasses 'getNameViaLens) `shouldSatisfy` isSuccess+        it "getting composition via lens is efficient as plain record function" $+            $(inspectTest $ 'getSyntaxViaLens === 'getSyntaxManually) `shouldSatisfy` isSuccess+        it "getting composition via lens doesn't have intermediate typeclasses" $+            $(inspectTest $ hasNoTypeClasses 'getSyntaxViaLens) `shouldSatisfy` isSuccess++matchMarkPrism :: Grade -> Maybe Int+matchMarkPrism = preview _Mark++matchMarkManual :: Grade -> Maybe Int+matchMarkManual grade = case grade of+    Mark n -> Just n+    _other -> Nothing++prismSpecs :: Spec+prismSpecs = describe "Prism" $ do+    describe "preview" $ do+        xit "preview _Ctor x ≡ case (Ctor _) of" $+            $(inspectTest $ 'matchMarkPrism === 'matchMarkManual) `shouldSatisfy` isSuccess++-- Helper functions++isSuccess :: Result -> Bool+isSuccess (Success _) = True+isSuccess (Failure _) = False
+ test/Test/Prolens/Property.hs view
@@ -0,0 +1,68 @@+module Test.Prolens.Property+    ( lensPropertySpecs+    , typeclassesPropertySpecs+    ) where++import Test.Hspec (Spec, describe, it)+import Test.Hspec.Hedgehog (PropertyT, forAll, forAllWith, hedgehog, (===))++import Prolens+import Test.Data (genFun, genFunction, genHaskeller, genInt, genName, nameL)+++lensPropertySpecs :: Spec+lensPropertySpecs = describe "Lens Laws" $ do+    it "view lens (set lens value source) ≡ value" $ hedgehog $ do+        source <- forAll genHaskeller+        value <- forAll genName+        view nameL (set nameL value source) === value+    it "set lens (view lens source) source ≡ source" $ hedgehog $ do+        source <- forAll genHaskeller+        set nameL (view nameL source) source === source+    it "set lens valueNew (set lens value source) ≡ set lens valueNew source" $ hedgehog $ do+        source <- forAll genHaskeller+        value <- forAll genName+        valueNew <- forAll genName+        set nameL valueNew (set nameL value source) === set nameL valueNew source++typeclassesPropertySpecs :: Spec+typeclassesPropertySpecs = describe "Class Laws" -- $ do+    profunctorsSpec++profunctorsSpec :: Spec+profunctorsSpec = describe "Profunctor" $ do+    describe "(->)" $ do+        it "Identity: dimap id id ≡ id" $ hedgehog $ do+            f <- forAllWith (const "f") genFunction+            x <- forAll genInt+            dimap id id f x === f x+        it "Composition: dimap (ab . bc) (yz . xy) ≡ dimap bc yz . dimap ab xy" $ hedgehog $ do++            f  <- forAllWith (const "f")  genFunction+            ab <- forAllWith (const "ab") genFunction+            bc <- forAllWith (const "bc") genFunction+            xy <- forAllWith (const "xy") genFunction+            yz <- forAllWith (const "xy") genFunction++            n <- forAll genInt+            dimap (ab . bc) (yz . xy) f n === (dimap bc yz . dimap ab xy) f n+    describe "Fun" $ do+        it "Identity: dimap id id ≡ id" $ hedgehog $ do+            f <- forAllWith (const "f") genFun+            eqFun (dimap id id f) f+        it "Composition: dimap (ab . bc) (yz . xy) ≡ dimap bc yz . dimap ab xy" $ hedgehog $ do++            f  <- forAllWith (const "f")  genFun+            ab <- forAllWith (const "ab") genFunction+            bc <- forAllWith (const "bc") genFunction+            xy <- forAllWith (const "xy") genFunction+            yz <- forAllWith (const "xy") genFunction++            eqFun+                (dimap (ab . bc) (yz . xy) f)+                (dimap bc yz $ dimap ab xy f)++eqFun :: Fun Maybe Int Int -> Fun Maybe Int Int -> PropertyT IO ()+eqFun fun1 fun2 = do+    x <- forAll genInt+    unFun fun1 x === unFun fun2 x