Author:Kenta Inoue
We introduce the Lens type, which is used for handling objects in functional programming, using the functional programming language Haskell.
First, we show the types of getter and setter, which are the features of object-oriented programming.
Types of getter and setter
Let s
be the type of an object and a
be the
type of a variable, the getter function get
and the
setter function set
have the following types.
get :: s -> a
set :: s -> a -> s
However, get
and set
need to satisfy for
any object s
and input a
, b
.
get (set s a) = a
set s (get s) = s
set (set s b) a = set s a
The first is that getting after setting the value a in the object, should output the same value. The second is that setting the value obtained from get directly using set should result in an object equal to the original one. The third is that repeating multiple set operations is equivalent to applying only the last set.
make getter and setter from Lens
While functions like set
and get
are
straightforward for handling objects, we can create a function of
type MyLens
by combining these two, as shown below.
type MyLens s a = forall f. Functor f => (a -> f a) -> s -> f s
In fact, the functions get
and set
can be
defined from MyLens
, and vice versa.
{- get & set -> lens -}
mkMyLens :: (s -> a) -> (s -> a -> s) -> MyLens s a
mkMyLens get set f s = set s <$> f (get s)
{- lens -> get -}
mkGet :: MyLens s a -> s -> a
mkGet lens = getConst . lens Const
{- lens -> set -}
mkSet :: MyLens s a -> s -> a -> s
mkSet lens s a = runIdentity $ lens (\_ -> Identity a) s
Furthermore, the following properties also hold for any
get
and set
.
mkGet (mkLens get set) s = get s
mkSet (mkLens get set) s a = set s a
In short, both get
and set
can be replaced
using a single MyLens
type.
However, Lens
type in Haskell is different from this
MyLens
.
Lens in Haskell
In Haskell, we generally use Lens
type which is
generalized from the previous MyLens
type.
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
In fact, MyLens
can be written from Lens
and is already defined in libraries named as Lens'
.
type MyLens s a = Lens s s a a
type Lens' s a = Lens s s a a
the relation between getter, setter and Lens
The corresponding types for Getter and Setter for
this Lens
are:
Lens s t a b :: forall f. Functor f => (a -> f b) -> s -> f t
get :: s -> a
set :: s -> b -> t
Actually, get
and set
can also be defined
from Lens
, and vice versa.
{- get & set -> Lens -}
lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
lens sa sbt afb s = sbt s <$> afb (sa s)
{- Lens -> get -}
myView :: MonadReader s m => Lens s t a b -> m a
myView lens = Control.Monad.Reader.asks (getConst . lens Const)
myView' :: Lens s t a b -> s -> a
myView' = myView
{- view :: MonadReader s m => Getting a s a -> m a -}
{- type Getting r s a = (a -> Const r a) -> s -> Const r s -}
{- Lens -> set -}
set :: ASetter s t a b -> b -> s -> t
set l b = runIdentity . l (\_ -> Identity b)
mySet :: Lens s t a b -> s -> b -> t
mySet lens = flip $ set lens
{- type ASetter s t a b = (a -> Identity b) -> s -> Identity t -}
While view
is defined only on Lens s s a a
in the library, we can generalize it on Lens s t a b
as
myView
.
As with MyLens
, the following properties also hold for
any get
and set
.
myGet (lens get set) s = get s
mySet (lens get set) s a = set s a
conclusion
The Lens
type is beneficial for representing objects in
functional programming languages.