HORIZON HASKELLDocslts/ghc-9.10.xc74966e2026-09-27Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · c74966e · 2026-09-27

  • Packagelens-5.3.5
  • Exports45
  • LanguageHaskell2010
  • LicenceBSD-2-Clause
  • SourceType.hs

Isomorphism Lenses

4 declarations
typetype Iso s t a b = forall (p :: Type -> Type -> Type) (f :: Type -> Type). (Profunctor p, Functor f) => p a (f b) -> p s (f t)
#

Isomorphism families can be composed with another Lens using (.) and id.

Since every Iso is both a valid Lens and a valid Prism, the laws for those types imply the following laws for an Iso f:

f . from f ≡ id
from f . f ≡ id

Note: Composition with an Iso is index- and measure- preserving.

Isomorphism Construction

1 declaration

Consuming Isomorphisms

3 declarations
valuecloneIso :: AnIso s t a b -> Iso s t a b
#

Convert from AnIso back to any Iso.

This is useful when you need to store an isomorphism as a data type inside a container and later reconstitute it as an overloaded function.

See cloneLens or cloneTraversal for more information on why you might want to do this.

valuewithIso :: AnIso s t a b -> ((s -> a) -> (b -> t) -> r) -> r
#

Extract the two functions, one from s -> a and one from b -> t that characterize an Iso.

Working with isomorphisms

6 declarations
valueau :: Functor f => AnIso s t a b -> ((b -> t) -> f s) -> f a
#

Based on ala from Conor McBride's work on Epigram.

This version is generalized to accept any Iso, not just a newtype.

Example1 expression
au (_Wrapping Sum) foldMap [1,2,3,4]10

You may want to think of this combinator as having the following, simpler type:

au :: AnIso s t a b -> ((b -> t) -> e -> s) -> e -> a
au = xplat . from
valueauf :: (Functor f, Functor g) => AnIso s t a b -> (f t -> g s) -> f b -> g a
#

Based on ala' from Conor McBride's work on Epigram.

This version is generalized to accept any Iso, not just a newtype.

For a version you pass the name of the newtype constructor to, see alaf.

Example1 expression
auf (_Wrapping Sum) (foldMapOf both) Prelude.length ("hello","world")10

Mnemonically, the German auf plays a similar role to à la, and the combinator is au with an extra function argument:

auf :: Iso s t a b -> ((r -> t) -> e -> s) -> (r -> b) -> e -> a

but the signature is general.

Note: The direction of the Iso required for this function changed in lens 4.18 to match up with the behavior of au. For the old behavior use xplatf or for a version that is compatible across both old and new versions of lens you can just use coerce!

valuexplatf :: Optic (Costar f) g s t a b -> (f a -> g b) -> f s -> g t
#

xplatf = auf . from but with a nicer signature.

Example1 expression
xplatf (_Unwrapping Sum) (foldMapOf both) Prelude.length ("hello","world")10
xplatf :: Iso s t a b -> ((r -> a) -> e -> b) -> (r -> s) -> e -> t

Common Isomorphisms

valuesimple :: p a (f a) -> p a (f a)
#

Composition with this isomorphism is occasionally useful when your Lens, Control.Lens.Traversal.Traversal or Iso has a constraint on an unused argument to force that argument to agree with the type of a used argument and avoid ScopedTypeVariables or other ugliness.

valuenon :: Eq a => a -> Iso' (Maybe a) a
#

If v is an element of a type a, and a' is a sans the element v, then non v is an isomorphism from Maybe a' to a.

non ≡ non' . only

Keep in mind this is only a real isomorphism if you treat the domain as being Maybe (a sans v).

This is practically quite useful when you want to have a Data.Map.Map where all the entries should have non-zero values.

Example1 expression
Map.fromList [("hello",1)] & at "hello" . non 0 +~ 2fromList [("hello",3)]
Example1 expression
Map.fromList [("hello",1)] & at "hello" . non 0 -~ 1fromList []
Example1 expression
Map.fromList [("hello",1)] ^. at "hello" . non 01
Example1 expression
Map.fromList [] ^. at "hello" . non 00

This combinator is also particularly useful when working with nested maps.

e.g. When you want to create the nested Data.Map.Map when it is missing:

Example1 expression
Map.empty & at "hello" . non Map.empty . at "world" ?~ "!!!"fromList [("hello",fromList [("world","!!!")])]

and when have deleting the last entry from the nested Data.Map.Map mean that we should delete its entry from the surrounding one:

Example1 expression
Map.fromList [("hello",Map.fromList [("world","!!!")])] & at "hello" . non Map.empty . at "world" .~ NothingfromList []

It can also be used in reverse to exclude a given value:

Example1 expression
non 0 # rem 10 4Just 2
Example1 expression
non 0 # rem 10 5Nothing
valuenon' :: APrism' a () -> Iso' (Maybe a) a
#

non' p generalizes non (p # ()) to take any unit Prism

This function generates an isomorphism between Maybe (a | isn't p a) and a.

Example1 expression
Map.singleton "hello" Map.empty & at "hello" . non' _Empty . at "world" ?~ "!!!"fromList [("hello",fromList [("world","!!!")])]
Example1 expression
Map.fromList [("hello",Map.fromList [("world","!!!")])] & at "hello" . non' _Empty . at "world" .~ NothingfromList []
valueanon :: a -> (a -> Bool) -> Iso' (Maybe a) a
#

anon a p generalizes non a to take any value and a predicate.

This function assumes that p a holds True and generates an isomorphism between Maybe (a | not (p a)) and a.

Example1 expression
Map.empty & at "hello" . anon Map.empty Map.null . at "world" ?~ "!!!"fromList [("hello",fromList [("world","!!!")])]
Example1 expression
Map.fromList [("hello",Map.fromList [("world","!!!")])] & at "hello" . anon Map.empty Map.null . at "world" .~ NothingfromList []
valueenum :: Enum a => Iso' Int a
#

This isomorphism can be used to convert to or from an instance of Enum.

Example1 expression
LT^.from enum0
Example1 expression
97^.enum :: Char'a'

Note: this is only an isomorphism from the numeric range actually used and it is a bit of a pleasant fiction, since there are questionable Enum instances for Double, and Float that exist solely for [1.0 .. 4.0] sugar and the instances for those and Integer don't cover all values in their range.

valuecurried
  1. :: (Profunctor p, Functor f2)
  2. => p (a -> b -> c) (f2 (d -> e -> f1))
  3. -> p ((a, b) -> c) (f2 ((d, e) -> f1))
#

The canonical isomorphism for currying and uncurrying a function.

curried = iso curry uncurry
Example1 expression
(fst^.curried) 3 43
Example1 expression
view curried fst 3 43
valueflipped
  1. :: (Profunctor p, Functor f)
  2. => p (b -> a -> c) (f (b' -> a' -> c'))
  3. -> p (a -> b -> c) (f (a' -> b' -> c'))
#

The isomorphism for flipping a function.

Example1 expression
((,)^.flipped) 1 2(2,1)
classclass Reversing t where
#

This class provides a generalized notion of list reversal extended to other containers.

Methods

Instances13Reversing, …
valuereversed :: Reversing a => Iso' a a
#

An Iso between a list, ByteString, Text fragment, etc. and its reversal.

Example1 expression
"live" ^. reversed"evil"
Example1 expression
"live" & reversed %~ ('d':)"lived"
valueinvoluted :: (a -> a) -> Iso' a a
#

Given a function that is its own inverse, this gives you an Iso using it in both directions.

involuted ≡ join iso
Example1 expression
"live" ^. involuted reverse"evil"
Example1 expression
"live" & involuted reverse %~ ('d':)"lived"

Uncommon Isomorphisms

valuemagma
  1. :: LensLike (Mafic a b) s t a b
  2. -> Iso s u (Magma Int t b a) (Magma j u c c)
#

This isomorphism can be used to inspect a Traversal to see how it associates the structure and it can also be used to bake the Traversal into a Magma so that you can traverse over it multiple times.

datadata Magma i t b a where
#

This provides a way to peek at the internal structure of a Control.Lens.Traversal.Traversal or Control.Lens.Traversal.IndexedTraversal

Instances7FoldableWithIndex, FunctorWithIndex, TraversableWithIndex, Functor, Foldable, Traversable, …

Contravariant functors

Profunctors

4 declarations
classclass Profunctor (p :: Type -> Type -> Type) where
#

Formally, the class Profunctor represents a profunctor from Hask -> Hask.

Intuitively it is a bifunctor where the first argument is contravariant and the second argument is covariant.

You can define a Profunctor by either defining dimap or by defining both lmap and rmap.

If you supply dimap, you should ensure that:

dimap id id ≡ id

If you supply lmap and rmap, ensure:

lmap id ≡ id
rmap id ≡ id

If you supply both, you should also ensure:

dimap f g ≡ lmap f . rmap g

These ensure by parametricity:

dimap (f . g) (h . i) ≡ dimap g h . dimap f i
lmap (f . g) ≡ lmap g . lmap f
rmap (f . g) ≡ rmap f . rmap g

Methods

  • dimap :: (a -> b) -> (c -> d) -> p b c -> p a d

    Map over both arguments at the same time.

    dimap f g ≡ lmap f . rmap g
  • lmap :: (a -> b) -> p b c -> p a c

    Map the first argument contravariantly.

    lmap f ≡ dimap f id
  • rmap :: (b -> c) -> p a b -> p a c

    Map the second argument covariantly.

    rmap ≡ dimap id
Instances46Profunctor, …

Bifunctors

3 declarations

Coercions

1 declaration
valuecoerced :: (Coercible s a, Coercible t b) => Iso s t a b
#

Data types that are representationally equal are isomorphic.

This is only available on GHC 7.8+