Copyright | (C) 2012-16 Edward Kmett |
---|---|

License | BSD-style (see the file LICENSE) |

Maintainer | Edward Kmett <ekmett@gmail.com> |

Stability | provisional |

Portability | non-portable |

Safe Haskell | Trustworthy |

Language | Haskell2010 |

## Synopsis

- type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)
- type Prism' s a = Prism s s a a
- type APrism s t a b = Market a b a (Identity b) -> Market a b s (Identity t)
- type APrism' s a = APrism s s a a
- prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
- prism' :: (b -> s) -> (s -> Maybe a) -> Prism s s a b
- withPrism :: APrism s t a b -> ((b -> t) -> (s -> Either t a) -> r) -> r
- clonePrism :: APrism s t a b -> Prism s t a b
- outside :: Representable p => APrism s t a b -> Lens (p t r) (p s r) (p b r) (p a r)
- aside :: APrism s t a b -> Prism (e, s) (e, t) (e, a) (e, b)
- without :: APrism s t a b -> APrism u v c d -> Prism (Either s u) (Either t v) (Either a c) (Either b d)
- below :: Traversable f => APrism' s a -> Prism' (f s) (f a)
- isn't :: APrism s t a b -> s -> Bool
- matching :: APrism s t a b -> s -> Either t a
- _Left :: Prism (Either a c) (Either b c) a b
- _Right :: Prism (Either c a) (Either c b) a b
- _Just :: Prism (Maybe a) (Maybe b) a b
- _Nothing :: Prism' (Maybe a) ()
- _Void :: Prism s s a Void
- _Show :: (Read a, Show a) => Prism' String a
- only :: Eq a => a -> Prism' a ()
- nearly :: a -> (a -> Bool) -> Prism' a ()
- class Profunctor p => Choice (p :: Type -> Type -> Type) where

# Prisms

type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t) #

A `Prism`

`l`

is a `Traversal`

that can also be turned
around with `re`

to obtain a `Getter`

in the
opposite direction.

There are three laws that a `Prism`

should satisfy:

First, if I `re`

or `review`

a value with a `Prism`

and then `preview`

or use (`^?`

), I will get it back:

`preview`

l (`review`

l b) ≡`Just`

b

Second, if you can extract a value `a`

using a `Prism`

`l`

from a value `s`

, then the value `s`

is completely described by `l`

and `a`

:

`preview`

l s ≡`Just`

a ⟹`review`

l a ≡ s

Third, if you get non-match `t`

, you can convert it result back to `s`

:

`matching`

l s ≡`Left`

t ⟹`matching`

l t ≡`Left`

s

The first two laws imply that the `Traversal`

laws hold for every `Prism`

and that we `traverse`

at most 1 element:

`lengthOf`

l x`<=`

1

It may help to think of this as an `Iso`

that can be partial in one direction.

Every `Prism`

is a valid `Traversal`

.

For example, you might have a

allows you to always
go from a `Prism'`

`Integer`

`Natural`

`Natural`

to an `Integer`

, and provide you with tools to check if an `Integer`

is
a `Natural`

and/or to edit one if it is.

`nat`

::`Prism'`

`Integer`

`Natural`

`nat`

=`prism`

`toInteger`

`$`

\ i -> if i`<`

0 then`Left`

i else`Right`

(`fromInteger`

i)

Now we can ask if an `Integer`

is a `Natural`

.

`>>>`

Just 5`5^?nat`

`>>>`

Nothing`(-5)^?nat`

We can update the ones that are:

`>>>`

(-3,8)`(-3,4) & both.nat *~ 2`

And we can then convert from a `Natural`

to an `Integer`

.

`>>>`

5`5 ^. re nat -- :: Natural`

Similarly we can use a `Prism`

to `traverse`

the `Left`

half of an `Either`

:

`>>>`

Left 5`Left "hello" & _Left %~ length`

or to construct an `Either`

:

`>>>`

Left 5`5^.re _Left`

such that if you query it with the `Prism`

, you will get your original input back.

`>>>`

Just 5`5^.re _Left ^? _Left`

Another interesting way to think of a `Prism`

is as the categorical dual of a `Lens`

-- a co-`Lens`

, so to speak. This is what permits the construction of `outside`

.

Note: Composition with a `Prism`

is index-preserving.

type APrism s t a b = Market a b a (Identity b) -> Market a b s (Identity t) #

If you see this in a signature for a function, the function is expecting a `Prism`

.

# Constructing Prisms

# Consuming Prisms

withPrism :: APrism s t a b -> ((b -> t) -> (s -> Either t a) -> r) -> r #

Convert `APrism`

to the pair of functions that characterize it.

clonePrism :: APrism s t a b -> Prism s t a b #

Clone a `Prism`

so that you can reuse the same monomorphically typed `Prism`

for different purposes.

See `cloneLens`

and `cloneTraversal`

for examples of why you might want to do this.

outside :: Representable p => APrism s t a b -> Lens (p t r) (p s r) (p b r) (p a r) #

aside :: APrism s t a b -> Prism (e, s) (e, t) (e, a) (e, b) #

Use a `Prism`

to work over part of a structure.

without :: APrism s t a b -> APrism u v c d -> Prism (Either s u) (Either t v) (Either a c) (Either b d) #

below :: Traversable f => APrism' s a -> Prism' (f s) (f a) #

`lift`

a `Prism`

through a `Traversable`

functor, giving a Prism that matches only if all the elements of the container match the `Prism`

.

`>>>`

[]`[Left 1, Right "foo", Left 4, Right "woot"]^..below _Right`

`>>>`

[["hail hydra!","foo","blah","woot"]]`[Right "hail hydra!", Right "foo", Right "blah", Right "woot"]^..below _Right`

matching :: APrism s t a b -> s -> Either t a #

Retrieve the value targeted by a `Prism`

or return the
original value while allowing the type to change if it does
not match.

`>>>`

Right 12`matching _Just (Just 12)`

`>>>`

Left Nothing`matching _Just (Nothing :: Maybe Int) :: Either (Maybe Bool) Int`

# Common Prisms

_Left :: Prism (Either a c) (Either b c) a b #

This `Prism`

provides a `Traversal`

for tweaking the `Left`

half of an `Either`

:

`>>>`

Left 3`over _Left (+1) (Left 2)`

`>>>`

Right 2`over _Left (+1) (Right 2)`

`>>>`

""`Right 42 ^._Left :: String`

`>>>`

"hello"`Left "hello" ^._Left`

It also can be turned around to obtain the embedding into the `Left`

half of an `Either`

:

`>>>`

Left 5`_Left # 5`

`>>>`

Left 5`5^.re _Left`

_Right :: Prism (Either c a) (Either c b) a b #

This `Prism`

provides a `Traversal`

for tweaking the `Right`

half of an `Either`

:

`>>>`

Left 2`over _Right (+1) (Left 2)`

`>>>`

Right 3`over _Right (+1) (Right 2)`

`>>>`

"hello"`Right "hello" ^._Right`

`>>>`

[]`Left "hello" ^._Right :: [Double]`

It also can be turned around to obtain the embedding into the `Right`

half of an `Either`

:

`>>>`

Right 5`_Right # 5`

`>>>`

Right 5`5^.re _Right`

_Just :: Prism (Maybe a) (Maybe b) a b #

This `Prism`

provides a `Traversal`

for tweaking the target of the value of `Just`

in a `Maybe`

.

`>>>`

Just 3`over _Just (+1) (Just 2)`

Unlike `traverse`

this is a `Prism`

, and so you can use it to inject as well:

`>>>`

Just 5`_Just # 5`

`>>>`

Just 5`5^.re _Just`

Interestingly,

m`^?`

`_Just`

≡ m

`>>>`

Just x`Just x ^? _Just`

`>>>`

Nothing`Nothing ^? _Just`

_Show :: (Read a, Show a) => Prism' String a #

This is an improper prism for text formatting based on `Read`

and `Show`

.

This `Prism`

is "improper" in the sense that it normalizes the text formatting, but round tripping
is idempotent given sane `Read`

/`Show`

instances.

`>>>`

"2"`_Show # 2`

`>>>`

Just EQ`"EQ" ^? _Show :: Maybe Ordering`

`_Show`

≡`prism'`

`show`

`readMaybe`

nearly :: a -> (a -> Bool) -> Prism' a () #

This `Prism`

compares for approximate equality with a given value and a predicate for testing,
an example where the value is the empty list and the predicate checks that a list is empty (same
as `_Empty`

with the `AsEmpty`

list instance):

`>>>`

[]`nearly [] null # ()`

`>>>`

Nothing`[1,2,3,4] ^? nearly [] null`

`nearly`

[]`null`

::`Prism'`

[a] ()

To comply with the `Prism`

laws the arguments you supply to `nearly a p`

are somewhat constrained.

We assume `p x`

holds iff `x ≡ a`

. Under that assumption then this is a valid `Prism`

.

This is useful when working with a type where you can test equality for only a subset of its values, and the prism selects such a value.

# Prismatic profunctors

class Profunctor p => Choice (p :: Type -> Type -> Type) where #

The generalization of `Costar`

of `Functor`

that is strong with respect
to `Either`

.

Note: This is also a notion of strength, except with regards to another monoidal structure that we can choose to equip Hask with: the cocartesian coproduct.

left' :: p a b -> p (Either a c) (Either b c) #

Laws:

`left'`

≡`dimap`

swapE swapE`.`

`right'`

where swapE ::`Either`

a b ->`Either`

b a swapE =`either`

`Right`

`Left`

`rmap`

`Left`

≡`lmap`

`Left`

`.`

`left'`

`lmap`

(`right`

f)`.`

`left'`

≡`rmap`

(`right`

f)`.`

`left'`

`left'`

`.`

`left'`

≡`dimap`

assocE unassocE`.`

`left'`

where assocE ::`Either`

(`Either`

a b) c ->`Either`

a (`Either`

b c) assocE (`Left`

(`Left`

a)) =`Left`

a assocE (`Left`

(`Right`

b)) =`Right`

(`Left`

b) assocE (`Right`

c) =`Right`

(`Right`

c) unassocE ::`Either`

a (`Either`

b c) ->`Either`

(`Either`

a b) c unassocE (`Left`

a) =`Left`

(`Left`

a) unassocE (`Right`

(`Left`

b)) =`Left`

(`Right`

b) unassocE (`Right`

(`Right`

c)) =`Right`

c

right' :: p a b -> p (Either c a) (Either c b) #

Laws:

`right'`

≡`dimap`

swapE swapE`.`

`left'`

where swapE ::`Either`

a b ->`Either`

b a swapE =`either`

`Right`

`Left`

`rmap`

`Right`

≡`lmap`

`Right`

`.`

`right'`

`lmap`

(`left`

f)`.`

`right'`

≡`rmap`

(`left`

f)`.`

`right'`

`right'`

`.`

`right'`

≡`dimap`

unassocE assocE`.`

`right'`

where assocE ::`Either`

(`Either`

a b) c ->`Either`

a (`Either`

b c) assocE (`Left`

(`Left`

a)) =`Left`

a assocE (`Left`

(`Right`

b)) =`Right`

(`Left`

b) assocE (`Right`

c) =`Right`

(`Right`

c) unassocE ::`Either`

a (`Either`

b c) ->`Either`

(`Either`

a b) c unassocE (`Left`

a) =`Left`

(`Left`

a) unassocE (`Right`

(`Left`

b)) =`Left`

(`Right`

b) unassocE (`Right`

(`Right`

c)) =`Right`

c