diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -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
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -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`
diff --git a/prolens.cabal b/prolens.cabal
new file mode 100644
--- /dev/null
+++ b/prolens.cabal
@@ -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
diff --git a/src/Prolens.hs b/src/Prolens.hs
new file mode 100644
--- /dev/null
+++ b/src/Prolens.hs
@@ -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
diff --git a/test/Doctest.hs b/test/Doctest.hs
new file mode 100644
--- /dev/null
+++ b/test/Doctest.hs
@@ -0,0 +1,15 @@
+module Main (main) where
+
+import Test.DocTest (doctest)
+
+
+main :: IO ()
+main = doctest
+    $ "-XLambdaCase"
+    : "-XInstanceSigs"
+    : "-XScopedTypeVariables"
+    : "-XTupleSections"
+    : "-XTypeApplications"
+    :
+    [ "src/Prolens.hs"
+    ]
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -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
diff --git a/test/Test/Data.hs b/test/Test/Data.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Data.hs
@@ -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
+    ]
diff --git a/test/Test/Prolens.hs b/test/Test/Prolens.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Prolens.hs
@@ -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
diff --git a/test/Test/Prolens/Inspection.hs b/test/Test/Prolens/Inspection.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Prolens/Inspection.hs
@@ -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
diff --git a/test/Test/Prolens/Property.hs b/test/Test/Prolens/Property.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Prolens/Property.hs
@@ -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
