I would like to know, what is natural transformation in Haskell. The natural transformation is described with the following signature:
F[a] ~> G[a]
For example, I could transform:
Maybe a ~> List a
Right?
What about IO
, it is impossible to do natural transformation right?
What is the purpose of the natural transformation?
In category theory, a branch of mathematics, a natural transformation provides a way of transforming one functor into another while respecting the internal structure (i.e., the composition of morphisms) of the categories involved. Hence, a natural transformation can be considered to be a "morphism of functors".
A natural isomorphism from F to G is an isomorphism in the functor category Funct(C,D), that is, a natural transformation η:F→G for which there exists a natural transformation ξ:G→F such that the compositions ξ∘η=1F and η∘ξ=1G are identity natural transformations.
A natural transformation, without getting into the category theory behind it, is really just a polymorphic function.
Prelude> :set -XRankNTypes
Prelude> :set -XTypeOperators
Prelude> type (~>) f g = forall x. f x -> g x
The ~>
operator maps a type constructor to another type constructor, in such a way that it works for any given argument to the first type constructor. The TypeOperator
extension is what lets us use ~>
instead of a name like NatTrans
; the RankNTypes
is what lets us use forall
in the definition so that the caller can choose which type f
and g
will be applied to.
Here is an example of a natural transformation from Maybe
to List
, which takes a Maybe a
for any type a
, and produces an equivalent list (by returning either an empty list or the wrapped value as a singleton list).
Prelude> :{
Prelude| m2l :: Maybe ~> [] -- Maybe a -> [a]
Prelude| m2l Nothing = []
Prelude| m2l (Just x) = [x]
Prelude| :}
Prelude> m2l Nothing
[]
Prelude> m2l (Just 3)
[3]
Prelude> m2l (Just 'c')
"c"
An "inverse" would be l2m :: [] ~> Maybe
, with l2m [] = Nothing
and l2m (x:_) = Just x
. ( I put inverse in quotes because m2l (l2m [1,2,3]) /= [1,2,3]
)
Nothing prevents you from using IO
as either of the type constructors (although if IO
is on the left, it must be on the right as well).
foo :: IO ~> IO
foo a = putStrLn "hi" >> a
Then
> foo (putStrLn "foo")
hi
foo
> foo (return 3)
hi
3
Another example would be to view length
as a natural transformation from []
to Const Int
(adapted from https://bartoszmilewski.com/2015/04/07/natural-transformations/, which I highly recommend reading):
-- Isomorphic to builtin length, as Const Int is isomorphic to Int
-- Requires importing Data.Functor.Const
length' :: [] ~> Const Int
length' [] = Const 0
length' (x:xs) = Const (1 + getConst (length' xs))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With