Lens
Overview
Lenses allow us to ”zoom in” on one part of a structure.
They are naturally composable, because they are just functions.
type Lens s a = forall f. Functor f => (a -> f a) -> s -> fs
Lens law
get set: You get back what you set: get l (set l sa) = a
set get: Setting what you got does not change anything: set l s (get l s) = s
set set: Setting twice is the same as setting once: set l (set l s a') a = set l s a
Example code
code:lenses.hs
{-# LANGUAGE RankNTypes #-}
import Data.Map
import qualified Data.Map as M
import Control.Category
import Prelude hiding ((.), id)
import Control.Monad.Identity
import Control.Applicative (Const(..))
type Lens s a = forall f . Functor f => (a -> f a) -> s -> f s
_1 :: Lens (a, b) a
_1 = lens fst (\(_, b) a -> (a,b))
_2 :: Lens (a, b) b
_2 = lens snd (\(a, _) b -> (a,b))
-- Get data
get :: Lens s a -> s -> a
get sa s = getConst (sa Const s)
-- Specific over
set :: Lens s a -> s -> a -> s
set sa s a = over sa (const a) s
-- Smart constructor for lens
lens :: (s -> a) -> (s -> a -> s) -> Lens s a
lens gt st f s = st s <$> f (gt s)
-- Update data
-- Since it's (a -> a) , we don't need full power of lens
-- Identity is enough
over :: Lens s a -> (a -> a) -> s -> s
over sa f s =
runIdentity (sa (Identity . f)s)
data Sign = Plus | Zero | Minus deriving Show
sign :: Lens Int Sign
sign = lens gt st
where
gt n
| n > 0 = Plus
| n == 0 = Zero
| otherwise = Minus
st n Plus = if n == 0 then 1 else abs n
st _ Zero = 0
st n Minus = if n == 0 then (-1) else - (abs n)
at :: Ord k => k -> Lens (Map k v) (Maybe v)
at k = lens gt st
where
gt = M.lookup k
st m Nothing = M.delete k m
st m (Just v) = M.insert k v m
askName :: String -> IO String
askName n = do
putStrLn ("Old name was: " ++ n)
getLine
Useful links
Lenses In Pictures
#Optics
https://gyazo.com/e08ba34c983ef9d03eb99965b3a65dcf