`Profunctor (->)`

* [*Kleisli*](http://hackage.haskell.org/package/base/docs/Control-Arrow.html#t:Kleisli) arrows: `Functor f => Profunctor (Kleisli f)`

Note: An [*Arrow*](https://www.haskell.org/arrows/) is a generalisation of a computation with an input and an unrelated output. Monads can be lifted to arrows with the [*Kleisli*](https://wiki.haskell.org/Arrow_tutorial#Kleisli_Arrows) datatype.
For a more detailed introduction check
* the video ["The Extended Functor Family" by George Wilson](https://www.youtube.com/watch?v=JZPXzJ5tp9w)
* the School of Haskell article ["I love profunctors. They're so easy"](https://www.schoolofhaskell.com/user/liyang/profunctors)
## Profunctor based lenses with Mezzolens
[Mezzolens](https://hackage.haskell.org/package/mezzolens "The Mezzolens package") is a library written by Russell O'Connor.
A lens is defined here as a means to transform a structure, as function of, a transformation on one of its components. So you may combine the focused component mapping and the lens, over the initial structure, giving a transformed structure or a query result.
It takes the shape of a function of a profunctor on the component, giving a profunctor on the structure.
To characterize a lens ([lens generation](#the-lens-generator)) you need a component selection function, and a structure update or query function from the image of the selected component transformation.
Since uncurrying is needed for lens generators, we add the *Strong* class lenses over pairs, defined below, as requirement.
``` haskell
-- given 'a' the type of the target component of an input structure of type 'ta'
-- and 'b' the type of a transformed 'a', from which you produce 'tb'
type Optical p ta tb a b = p a b -> p ta tb
type Lens ta tb a b = forall p. Strong p => p a b -> p ta tb
-- where
class Profunctor p => Strong p where
_1 :: p a b -> p (a, c) (b, c)
_2 :: p a b -> p (c, a) (c, b)
-- simple lenses:
-- Lens' is the type of lenses that don't change the types of component and structure.
type Lens' ta a = Lens ta ta a a
```
The methods of a *Strong profunctor* can be seen as lenses that provide a transformation of a Pair, as function of, the transformation of one of its components.
Since a function is an instance of it, applying its lens to a function, gives as result a function over pairs:
``` haskell
Prelude> import Mezzolens as M
Prelude M> import Mezzolens.Profunctor as MP
Prelude M MP> import Data.Function ((&)) -- (&): backwards application
Prelude M MP>:t _1 (f :: a -> b)
:: (a, c) -> (b, c)
Prelude M MP>:t _2 (f :: a -> b)
:: (c, a) -> (c, b)
-- focusing with a lens and the function (+2), gets the structure updated
Prelude M MP> (1, 2) & _1 (+2)
(3, 2)
-- the function `M.get` with a lens, queries the focused component
Prelude M MP> (1, 2) & M.get _1
1
```
Lens generation:
``` haskell
-- from Mezzolens.Unchecked
-- lens :: (ta -> a) {- component selection -}
-> (b -> ta -> tb) {- struct. transf. with component image -}
-> Lens ta tb a b
-- ^ lens getter updater
```
As lenses are functions, they can compose, giving you access to items deep in a complex structure.
``` haskell
{-| file test-mezzo.hs -}
{-# LANGUAGE PackageImports #-}
import "mezzolens" Mezzolens (Lens', (^.), get, set)
import "mezzolens" Mezzolens.Unchecked (lens)
import Data.Function ((&)) -- (&): backwards application
data Arc = Arc {_degree, _minute, _second :: Int} deriving (Show)
data Location = Location {_latitude, _longitude :: Arc} deriving (Show)
-- structure to play with
locBcn = Location (arcFromDecimal 41.399423) (arcFromDecimal 2.128037)
-- decimal to sexagesimal latitude/longitude
arcFromDecimal :: Double -> Arc
arcFromDecimal v = Arc intPart mins secs
where
(intPart, fraction) = properFraction v
-- divide negative lat/lon fractions towards 0 with quotRem
(mins, secs) = truncate (fraction * 3600) `quotRem` 60
-- lens generators
degree, minute, second :: Lens' Arc Int
degree = lens _degree (\v arc -> arc {_degree = v})
minute = lens _minute (\v arc -> arc {_minute = v})
second = lens _second (\v arc -> arc {_second = v})
latitude :: Lens' Location Arc
latitude = lens _latitude (\v loc -> loc {_latitude = v})
-- composing lenses
lensDegreesFromLatitude, lensMinutesFromLatitude, lensSecondsFromLatitude :: Lens' Location Int
lensDegreesFromLatitude = latitude . degree
lensMinutesFromLatitude = latitude . minute
lensSecondsFromLatitude = latitude . second
-- get the focused component
degreeLat = locBcn & get lensDegreesFromLatitude
-- (^.) is an infix version of 'get'
-- update the focused component:
-- (+2) is a unary function (unary functions are instances of ''Profunctor'')
-- applying the lens to the Profunctor value (+2) will give us a profunctor of the same type (a function) on the structure
locTwoDegreesNordOfBcn = locBcn & lensDegreesFromLatitude (+2)
-- 'set' applies the lens to a constant function
-- set lens v = lens (const v)
```
With Ghci
``` haskell
$ ghci
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
Prelude> :l test-mezzo
[1 of 1] Compiling Main ( test-mezzo.hs, interpreted )
Ok, modules loaded: Main.
*Main> import Mezzolens as M
*Main M> :t M.get lensDegreesFromLatitude
M.get lensDegreesFromLatitude :: Location -> Int
*Main M> :t lensDegreesFromLatitude (+2)
lensDegreesFromLatitude (+2) :: Location -> Location
```
If you want a monadic transformation, wrap the monadic computation as a [*Kleisli arrow*](http://hackage.haskell.org/package/base/docs/Control-Arrow.html#t:Kleisli). Mezzolens brings an instance of Profunctor for Kleisli arrows.
Applying the lens to an *arrow* on the component returns an *arrow* on the structure, where you can get the monadic function inside with the Kleisli accessor *runKleisli*.
``` haskell
*Main M> import Control.Arrow as A
*Main M A> :{
*Main M A| let myMonadicCompoTransf :: Monad m => Int -> m Int
*Main M A| myMonadicCompoTransf = return . (+2)
*Main M A| :}
*Main M A> :t runKleisli . lensDegreesFromLatitude . Kleisli $ myMonadicCompoTransf
runKleisli . lensDegreesFromLatitude . Kleisli $ myMonadicCompoTransf
:: Monad m => Location -> m Location
```
But a monadic use is questionable unless you use [state lenses](https://hackage.haskell.org/package/mezzolens/docs/Mezzolens-State-Lazy.html) which I will not cover here.
### Prisms
Prisms are lenses where the component selection may not succeed, as they target parts of *sum types*.
Since the component selection results in an Either, we will require profunctors with the *Choice* class lenses over *Either*, needed in [prism generators](#the-prism-generator)
``` haskell
type Prism ta tb a b = forall p. Choice p => p a b -> p ta tb
-- where
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)
```
A Choice profunctor is one that brings lenses methods that let you transform an Either variant content with the input profunctor.
``` haskell
-- a Prism generator from Mezzolens.Unchecked
prism :: (ta -> Either tb a) {- component selection match -}
-> (b -> tb) {- struct. build up from component image -}
-> Prism ta tb a b
-- ^ prism match build
```
* the first *prism* generator parameter (component selection) should give either the component in case of success, or a result structure in case of fail.
* the second (final structure from component image) builds a sum type variant of *tb* from the image (type *b*) of the selected component
Example on the *head* of a List.
``` haskell
*Main> import Mezzolens as M
*Main M> import Mezzolens.Unchecked as MU
*Main M MU> :set -XLambdaCase
*Main M MU> :{
*Main M MU| let headMatch :: [a] -> Either [a] a
*Main M MU| headMatch = \case
*Main M MU| x : _ -> Right x -- target variant
*Main M MU| [] -> Left [] -- fail case giving a zero/empty structure
*Main M MU| let headBuild = \x -> [x]
*Main M MU| let _Head = MU.prism headMatch headBuild
*Main M MU| :}
-- Prism query (optional result)
*Main M MU> [locBcn] ^? (_Head . lensDegreesFromLatitude)
Just 41
-- Update on a target variant
*Main M MU> [locBcn] & (_Head . lensDegreesFromLatitude) (+2)
[Location {_latitude = Arc {_degree = 43, _minute = 23, _second = 57}, _longitude = Arc {_degree = 2, _minute = 7, _second = 40}}]
-- Update on the non target variant
*Main M MU> [] & (_Head . lensDegreesFromLatitude) (+2)
[]
```
### Traverses and folds
The *Wandering* class and the function *gets*:
* *wander* offers a lens that let you traverse a *Traversable*, transforming its elements with the input profunctor.
* The *gets* function offers a means to get a fold of the [codomain](https://en.wikipedia.org/wiki/Codomain) (target set) of mapping the items focused by a traversal to a Monoid.
``` haskell
-- from Mezzolens.Profunctor
class (Strong p, Choice p) => Wandering p where
wander :: Traversable f => p a b -> p (f a) (f b)
-- from Mezzolens
gets :: Monoid r => Fold ta tb a b -> (a -> r) -> ta -> r
-- from Mezzolens.Optics
type Fold ta tb a b = forall p. (OutPhantom p, Wandering p) => Optical p ta tb a b
-- (OutPhantom p) means that the profunctor output type is coercible
```
with Ghci and the test code loaded
``` haskell
*Main> import Mezzolens as M
*Main M> import Mezzolens.Profunctor as MP
-- traverse a list, updating its elements
*Main M MP> [locBcn, locTwoDegreesNordOfBcn] & (MP.wander . lensDegreesFromLatitude) (+3)
[Location {_latitude = Arc {_degree = 44, _minute = 23, _second = 57}, _longitude = Arc {_degree = 2, _minute = 7, _second = 40}},Location {_latitude = Arc {_degree = 46, _minute = 23, _second = 57}, _longitude = Arc {_degree = 2, _minute = 7, _second = 40}}]
-- (toListOf): {toListOf lens = gets lens pure} it uses 'pure' to map the focused elements to a generic Applicative that when constraint to a List gives List singletons, folded with the List Monoid instance into a concatenation.
*Main M MP> [locBcn, locTwoDegreesNordOfBcn] & M.toListOf (MP.wander . lensDegreesFromLatitude) :: [Int]
[41,43]
-- (^..) is an infix version of toListOf
*Main M MP> [locBcn, locTwoDegreesNordOfBcn] ^.. (MP.wander . lensDegreesFromLatitude) :: [Int]
[41,43]
-- (sumOf): maps the focused elements to the Monoid Sum and combine. See next section.
*Main M MP> [locBcn, locTwoDegreesNordOfBcn] & M.sumOf (MP.wander . lensDegreesFromLatitude)
84
```
### Understanding folds like *sumOf*
We take *Sum* as the *gets* mapping function, fetching the result with its accessor *getSum*:
``` haskell
sumOf lens = getSum . gets lens Sum
gets :: Optical (SubStar (Constant r)) ta tb a b -> (a -> r) -> ta -> r
gets lens f = getConstant . h
where
Kleisli h = lens (Kleisli (Constant . f))
-- type SubStar = Kleisli -- from Mezzolens.Profunctor
-- substituting ''h'' extracting it with ''runKleisli''
gets lens f = getConstant . (runKleisli (lens (Kleisli (Constant . f))))
```
using *wander* as *lens* in the *gets* expression, then reducing *wander* on a Kleisli arrow:
``` haskell
Prelude ...> let g f = getConstant . (runKleisli (wander (Kleisli (Constant . f))))
-- from Mezzolens code:
instance Applicative f => Wandering (Kleisli f) where
wander (Kleisli h) = Kleisli (traverse h)
-- since there is an Applicative instance for Constant
Prelude ...> let g f = getConstant . (runKleisli (Kleisli (traverse (Constant . f))))
-- since {runKleisli . Kleisli == id}
Prelude ...> let g f = getConstant . (traverse (Constant . f))
Prelude ...> g Sum [1,2,3]
Sum {getSum = 6}
```
understanding *Constant* from Data.Functor.Constant
``` haskell
-- Constant has a phantom type parameter, so (Constant a) can be seen as a structure:
newtype Constant a b = Constant { getConstant :: a }
-- the (Constant a) instance for Applicative is defined only for 'a' Monoid
instance (Monoid a) => Applicative (Constant a) where
pure _ = Constant mempty -- applicative combinators will be ignored !!
Constant x <*> Constant y = Constant (x `mappend` y)
Prelude ...> :t traverse
traverse
:: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)
```
Since Constant is the Applicative type in the traversal, because in its instance *"pure"* ignores the combinator, whatever the traversable instance, you will get the Monoid composition instead, folding left to right as the container is traversed.
``` haskell
Prelude ...> :t traverse Constant
traverse Constant
:: (Traversable t, Monoid a) => t a -> Constant a (t b)
-- you will not get (t b) inside Constant
-- but ''mempty'' from (''pure'' traversable_combinator), ''mappend''ed to all a's
Prelude ...> traverse Constant [Sum 1, Sum 2, Sum 3]
Constant (Sum {getSum = 6})
```
as summary:
``` haskell
Prelude ...> :t gets wander
gets wander :: (Traversable f, Monoid r) => (a -> r) -> f a -> r
Prelude ...> :t gets wander Sum
gets wander Sum :: (Num a, Traversable f) => f a -> Sum a
Prelude ...> gets wander Sum [1,2,3]
Sum {getSum = 6}
```
### Some generators
#### The *lens* generator
``` haskell
type Optical p ta tb a b = p a b -> p ta tb
type Lens ta tb a b = forall p. Strong p => Optical p ta tb a b
lens :: (ta -> a) {- getter: component selection -}
-> (b -> ta -> tb) {- setter: struct. transf. with component image -}
-> Lens ta tb a b
-- we need to find parameters for ''dimap''
-- let's obtain an output map for our lens, pairing the setter parameters
> :t uncurry setter
:: (b, ta) -> tb
-- a matching contramap changing the component woud be
-- :: ta -> (a, ta)
-- with (&&&) from Arrow -- it pairs the results of two arrows of the same input type
-- from Control.Arrow.Arrow class
-- (&&&) :: Arrow a => a b c -> a b c' -> a b (c, c')
> :t getter &&& id -- since functions are instances of Arrow
:: ta -> (a, ta)
> :t dimap \
(getter &&& id) {- :: ta -> (a, ta) -} \
(uncurry setter) {- :: (b, ta) -> tb -}
:: Profunctor p => p (a, ta) (b, ta) -> p ta tb
```
With `dimap (getter &&& id) (uncurry setter)` you have a lens from the structure to a pair.
Composing it with `_1` (the lens on the first item of a pair) we further put the focus on the component transformation `p a b`, adding the requirement that the profunctor should implement the class Strong where `_1` is defined.
``` haskell
import Control.Arrow ((&&&), (|||))
type Optical p ta tb a b = p a b -> p ta tb
type Lens ta tb a b = forall p. Strong p => Optical p ta tb a b
-- the generator:
lens :: (ta -> a) {- component selection -}
-> (b -> ta -> tb) {- structure transf. with component image -}
-> Lens ta tb a b
lens getter setter = lensStructFromPair . _1
where
lensStructFromPair :: Profunctor p
=> p (a, ta) (b, ta)
-> p ta tb
lensStructFromPair = dimap (getter &&& id) (uncurry setter)
-- _1 is a lens on the first component of a pair
-- _1 :: Strong p => p a b -> p (a, c) (b, c)
```
#### The *prism* generator
Prisms target sum type variants
``` haskell
type Prism ta tb a b = forall p. Choice p => Optical p ta tb a b
prism :: (ta -> Either tb a) {- component selection match -}
-> (b -> tb) {- build final struct. from component image -}
-> Prism ta tb a b
-- `match` should give an (Either tb a) which means a component `a` in case of success or a zero result structure of type `tb` in case of fail
-- `build` should wrap the transformed component `b` in a variant of the sum type structure of type `tb`
-- we need to find parameters for ''dimap''
-- we take ''(match :: ta -> Either tb a)'' as the contramap operation
-- a matching map switching the comp. type with its image
-- would be ''(Either tb b -> tb)''
-- we have ''(build :: b -> tb)''
-- (|||) from ArrowChoice, splices the input types of two arrows of the same result type in an Either
-- from Control.Arrow.ArrowChoice class
-- (|||) :: ArrowChoice a => a b d -> a c d -> a (Either b c) d
> :t id ||| build -- since functions are instances of ArrowChoice
:: Either tb b -> tb
> :t dimap \
match {- :: ta -> Either tb a -} \
(id ||| build) {- :: Either tb b -> tb -}
:: Profunctor p => p (Either tb a) (Either tb b) -> p ta tb
prism match build = lensStructFromEither . _Right
where
lensStructFromEither :: Profunctor p
=> p (Either tb a) (Either tb b)
-> p ta tb
lensStructFromEither = dimap match (id ||| build)
-- _Right is a lens on the second domain of an Either
-- _Right :: Choice p => p a b -> p (Either c a) (Either c b)
```
With `dimap match (id ||| build)` you have a lens from the Structure to an Either having the component as Right or a Left `tb` structure in case of mismatch.
Composing it with the lens *\_Right*, will make possible to focus on the component, adding the requirement that the profunctor should also implement the class Choice where *\_Right* is defined.
#### Isos
Iso's are lenses that convert a profunctor on structures, on one over an Isomorphic representation
``` haskell
type Iso ta tb a b = forall p. Profunctor p => Optical p ta tb a b
-- the generator:
iso :: (ta -> a) -> (b -> tb) -> Iso ta tb a b
iso = dimap
-- as example, the lens _swap converts a profunctor on pairs into a profunctor over its swapped type
_swap :: Iso (a,b) (c,d) (b,a) (d,c)
_swap = iso swap swap
```