Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
A natural transformation is a concept from category theory for a mapping between two functors and their objects
that preserves a naturality condition. In Haskell the naturality condition boils down to parametricity, so a
natural transformation between two functors f
and g
is represented as
type NaturalTransformation f g = ∀a. f a → g a
This type appears in several Haskell libraries, most obviously in
natural-transformations. There are times, however,
when we crave more control. Sometimes what we want to do depends on which type a
is hiding in that f a
we're
given. Sometimes, in other words, we need an unnatural transformation.
This means we have to abandon parametricity for ad-hoc polymorphism, and that means type classes. There are two steps to defining a transformation:
- an instance of the base class
Transformation
declares the two functors being mapped, much like a function type signature, - while the actual mapping of values is performed by an arbitrary number of instances of the method
$
, a bit like multiple equation clauses that make up a single function definition.
The module is meant to be imported qualified, and the importing module will require at least the
FlexibleInstances
, MultiParamTypeClasses
, and TypeFamilies
language extensions to declare the appropriate
instances.
Synopsis
- class Transformation t where
- class Transformation t => At t x where
- apply :: t `At` x => t -> Domain t x -> Codomain t x
- data Compose t u = Compose t u
- newtype Mapped (f :: Type -> Type) t = Mapped t
- newtype Folded (f :: Type -> Type) t = Folded t
- newtype Traversed (f :: Type -> Type) t = Traversed t
- type family ComposeOuter (c :: Type -> Type) :: Type -> Type where ...
- type family ComposeInner (c :: Type -> Type) :: Type -> Type where ...
Documentation
>>>
{-# Language FlexibleInstances, MultiParamTypeClasses, TypeFamilies, TypeOperators #-}
>>>
import Transformation (Transformation)
>>>
import qualified Transformation
class Transformation t Source #
A Transformation
, natural or not, maps one functor to another.
For example, here's the declaration for a transformation that maps Maybe
to `[]`:
>>>
:{
data MaybeToList = MaybeToList instance Transformation MaybeToList where type Domain MaybeToList = Maybe type Codomain MaybeToList = [] :}
Instances
(Transformation t1, Transformation t2, Domain t1 ~ Domain t2) => Transformation (Either t1 t2) Source # | |
(Transformation t, Transformation u, Domain t ~ Codomain u) => Transformation (Compose t u) Source # | |
Transformation t => Transformation (Folded f t) Source # | |
Transformation t => Transformation (Mapped f t) Source # | |
(Transformation t, Codomain t ~ Compose m n) => Transformation (Traversed f t) Source # | |
Transformation (Fold p m) Source # | |
Transformation (Map p q) Source # | |
(Transformation t1, Transformation t2, Domain t1 ~ Domain t2) => Transformation (t1, t2) Source # | |
Transformation (Feeder a b f) Source # | |
Transformation (Traversal p q m) Source # | |
Transformation (Arrow p q x) Source # | |
class Transformation t => At t x where Source #
Before we can apply a Transformation
, we also need to declare At
which base types it is applicable and how
it works. If the transformation is natural, we'll need only one instance declaration.
>>>
:{
instance MaybeToList `Transformation.At` a where MaybeToList $ Just x = [x] MaybeToList $ Nothing = [] :}
>>>
MaybeToList Transformation.$ (Just True)
[True]
An unnatural Transformation
can behave differently depending on the base type and even on its value.
>>>
:{
instance {-# OVERLAPS #-} MaybeToList `At` Char where MaybeToList $ Just '\0' = [] MaybeToList $ Just c = [c] MaybeToList $ Nothing = [] :}
Instances
Composition of two transformations
Compose t u |
Instances
newtype Mapped (f :: Type -> Type) t Source #
Transformation under a Functor
Mapped t |
newtype Folded (f :: Type -> Type) t Source #
Transformation under a Foldable
Folded t |
newtype Traversed (f :: Type -> Type) t Source #
Transformation under a Traversable
Instances
(Transformation t, Codomain t ~ Compose m n) => Transformation (Traversed f t) Source # | |
(At t x, Traversable f, Codomain t ~ Compose m n, Applicative m) => At (Traversed f t) x Source # | |
type Codomain (Traversed f t) Source # | |
Defined in Transformation type Codomain (Traversed f t) = Compose (ComposeOuter (Codomain t)) (Compose f (ComposeInner (Codomain t))) | |
type Domain (Traversed f t) Source # | |
Defined in Transformation |
type family ComposeOuter (c :: Type -> Type) :: Type -> Type where ... Source #
ComposeOuter (Compose p q) = p |
type family ComposeInner (c :: Type -> Type) :: Type -> Type where ... Source #
ComposeInner (Compose p q) = q |