# Lenses

## Wofür brauchen wir das überhaupt?

Die Idee dahinter ist, dass man Zugriffsabstraktionen über Daten verknüpfen kann. Als einfachen Datenstruktur kann man einen Record mit der entsprechenden Syntax nehmen.

### Beispiel

    data Person = P { name :: String
                    , addr :: Address
                    , salary :: Int }
    data Address = A { road :: String
                     , city :: String
                     , postcode :: String }
    -- autogeneriert unten anderem: addr :: Person -> Address

        setName :: String -> Person -> Person
        setName n p = p { name = n } --record update notation

        setPostcode :: String -> Person -> Person
        setPostcode pc p
            = p { addr = addr p { postcode = pc } }
        -- update of a record inside a record

### Probleme

Probleme mit diesem Code:

- für 1-Dimensionale Felder ist die record-syntax ok.
- tiefere Ebenen nur umständlich zu erreichen
- eigentlich wollen wir nur pe in p setzen, müssen aber über addr etc. gehen.
- wir brauchen wissen über die “Zwischenstrukturen”, an denen wir nicht interessiert sind

### Was wir gern hätten

    data Person = P { name :: String
                    , addr :: Address
                    , salary :: Int }
    -- a lens for each field
    lname   :: Lens' Person String
    laddr   :: Lens' Person Adress
    lsalary :: Lens' Person Int
    -- getter/setter for them
    view    :: Lens' s a -> s -> a
    set     :: Lens' s a -> a -> s -> s
    -- lens-composition
    composeL :: Lens' s1 s2 -> Lens s2 a -> Lens' s1 a

### Wie uns das hilft

Mit diesen Dingen (wenn wir sie hätten) könnte man dann

    data Person = P { name :: String
                    , addr :: Address
                    , salary :: Int }
    data Address = A { road :: String
                     , city :: String
                     , postcode :: String }
    setPostcode :: String -> Person -> Person
    setPostcode pc p
        = set (laddr `composeL` lpostcode) pc p

machen und wäre fertig.

## Trivialer Ansatz

### Getter/Setter als Lens-Methoden

    data LensR s a = L { viewR :: s -> a
                       , setR  :: a -> s -> s }

    composeL (L v1 u1) (L v2 u2)
      = L (\s -> v2 (v1 s))
          (\a s -> u1 (u2 a (v1 s)) s)

### Wieso ist das schlecht?

- extrem ineffizient

Auslesen traversiert die Datenstruktur, dann wird die Funktion angewendet und zum setzen wird die Datenstruktur erneut traversiert:

    over :: LensR s a -> (a -> a) -> s -> s
    over ln f s = setR l (f (viewR l s)) s

- Lösung: modify-funktion hinzufügen

    data LensR s a
       = L { viewR :: s -> a
           , setR  :: a -> s -> s
           , mod   :: (a->a) -> s -> s
           , modM  :: (a->Maybe a) -> s -> Maybe s
           , modIO :: (a->IO a) -> s -> IO s }

Neues Problem: Für jeden Spezialfall muss die Lens erweitert werden.

### Something in common

Man kann alle Monaden abstrahieren. Functor reicht schon:

    data LensR s a
       = L { viewR :: s -> a
           , setR  :: a -> s -> s
           , mod   :: (a->a) -> s -> s
           , modF  :: Functor f => (a->f a) -> s -> f s }

Idee: Die 3 darüberliegenden durch modF ausdrücken.

### Typ einer Lens

Wenn man das berücksichtigt, dann hat einen Lens folgenden Typ:

    type Lens' s a = forall f. Functor f
                               => (a -> f a) -> s -> f s

Allerdings haben wir dann noch unseren getter/setter:

    data LensR s a = L { viewR :: s -> a
                       , setR :: a -> s -> s }

Stellt sich raus: Die sind isomorph! Auch wenn die von den Typen her komplett anders aussehen.

## Benutzen einer Lens als Setter

    set :: Lens' s a -> (a -> s -> s)
    set ln a s = --...umm...
    --:t ln => (a -> f a) -> s -> f s
    --            => get s out of f s to return it

Wir können für f einfach die “Identity”-Monade nehmen, die wir nachher wegcasten können.

    newtype Identity a = Identity a
    -- Id :: a -> Identity a

    runIdentity :: Identity s -> s
    runIdentity   (Identity x) = x

    instance Functor Identity where
        fmap f (Identity x) = Identity (f x)

somit ist set einfach nur

    set :: Lens' s a -> (a -> s -> s)
    set ln x s
       = runIdentity (ls set_fld s)
       where
         set_fld :: a -> Identity a
         set_fld _ = Identity x
         -- a was the OLD value.
         -- We throw that away and set the new value

oder kürzer (für nerds wie den Autor der Lens-Lib)

    set :: Lens' s a -> (a -> s -> s)
    set ln x = runIdentity . ln (Identity . const x)

## Benutzen einer Lens als Modify

Dasselbe wie Set, nur dass wir den Parameter nicht entsorgen, sondern in die mitgelieferte Funktion stopfen.

    over :: Lens' s a -> (a -> a) -> s -> s
    over ln f = runIdentity . ln (Identity . f)

## Benutzen einer Lens als Getter

    view :: Lens' s a -> (s -> a)
    view ln s = --...umm...
    --:t ln => (a -> f a) -> s -> f s
    --            => get a out of the (f s) return-value
    --            Wait, WHAT?

Auch hier gibt es einen netten Funktor. Wir packen das “a” einfach in das “f” und werfen das “s” am Ende weg.

    newtype Const v a = Const v

    getConst :: Const v a -> v
    getConst (Const x) = x

    instance Functor (Const v) where
        fmap f (Const x) = Const x
        -- throw f away. Nothing changes our const!

somit ergibt sich

    view :: Lens' s a -> (s -> a)
    view ln s
      = getConst (ln Const s)
                  -- Const :: s -> Const a s

oder nerdig

    view :: Lens' s a -> (s -> a)
    view ln = getConst . ln Const

## Lenses bauen

Nochmal kurz der Typ:

    type Lens' s a = forall f. Functor f
                         => (a -> f a) -> s -> f s

Für unser Personen-Beispiel vom Anfang:

    data Person = P { _name :: String, _salary :: Int }

    name :: Lens' Person String
    -- name :: Functor f => (String -> f String)
    --                    -> Person -> f Person

    name elt_fn (P n s)
      = fmap (\n' -> P n' s) (elt_fn n)
    -- fmap :: Functor f => (a->b) -> f a -> f b - der Funktor, der alles verknüpft
    -- \n' -> .. :: String -> Person - Funktion um das Element zu lokalisieren (WO wird ersetzt/gelesen/...)
    -- elt_fn n  :: f String         - Funktion um das Element zu verändern (setzen, ändern, ...)

Die Lambda-Funktion ersetzt einfach den Namen. Häufig sieht man auch

    name elt_fn (P n s)
      = (\n' -> P n' s) <$> (elt_fn n)
    --  |    Focus    |     |Function|

## Wie funktioniert das intern?

    view name (P {_name="Fred", _salary=100})
       -- inline view-function
    = getConst (name Const (P {_name="Fred", _salary=100})
       -- inline name
    = getConst (fmap (\n' -> P n' 100) (Const "Fred"))
       -- fmap f (Const x) = Const x - Definition von Const
    = getConst (Const "Fred")
       -- getConst (Const x) = x
    = "Fred"

Dieser Aufruf hat KEINE Runtime-Kosten, weil der Compiler direkt die Adresse des Feldes einsetzen kann. Der gesamte Boilerplate-Code wird vom Compiler wegoptimiert.

Dies gilt für jeden Funktor mit newtype, da das nur ein Typalias ist.

## Composing Lenses und deren Benutzung

Wie sehen denn die Typen aus?

Wir wollen ein

> Lens’ s1 s2 -> Lens’ s2 a -> Lens’ s1 a

Wir haben 2 Lenses

> ln1 :: (s2 -> f s2) -> (s1 -> f s1) ln2 :: (a -> f a) -> (s2 -> f s2)

wenn man scharf hinsieht, kann man die verbinden

> ln1 . ln2 :: (a -> f s) -> (s1 -> f s1)

und erhält eine Lens. Sogar die Gewünschte!
Somit ist Lens-Composition einfach nur Function-Composition (.).

## Automatisieren mit Template-Haskell

Der Code um die Lenses zu bauen ist für records immer Identisch:

    data Person = P { _name :: String, _salary :: Int }

    name :: Lens' Person String
    name elt_fn (P n s) = (\n' -> P n' s) <$> (elt_fn n)

Daher kann man einfach

    import Control.Lens.TH
    data Person = P { _name :: String, _salary :: Int }

    $(makeLenses ''Person)

nehmen, was einem eine Lens für “name” und eine Lens für “salary” generiert.
Mit anderen Templates kann man auch weitere Dinge steuern (etwa wofür Lenses generiert werden, welches Prefix (statt _) man haben will etc. pp.).

Will man das aber haben, muss man selbst in den Control.Lens.TH-Code schauen.

## Lenses für den Beispielcode

    import Control.Lens.TH

    data Person = P { _name :: String
                    , _addr :: Address
                    , _salary :: Int }
    data Address = A { _road :: String
                     , _city :: String
                     , _postcode :: String }

    $(makeLenses ''Person)
    $(makeLenses ''Address)

    setPostcode :: String -> Person -> Person
    setPostcode pc p = set (addr . postcode) pc p

## Shortcuts mit “Line-Noise”

    -- ...

    setPostcode :: String -> Person -> Person
    setPostcode pc p = addr . postcode .~ pc     $ p
    --                 |   Focus     |set|to what|in where

    getPostcode :: Person -> String
    getPostcode p = p   ^. $ addr . postcode
    --            |from|get|    Focus       |

Es gibt drölf-zillionen weitere Infix-Operatoren (für Folds, Listenkonvertierungen, -traversierungen, …)

## Virtuelle Felder

Man kann mit Lenses sogar Felder emulieren, die gar nicht da sind. Angenommen folgender Code:

    data Temp = T { _fahrenheit :: Float }

    $(makeLenses ''Temp)
    -- liefert Lens: fahrenheit :: Lens Temp Float

    centigrade :: Lens Temp Float
    centigrade centi_fn (T faren)
      = (\centi' -> T (cToF centi'))
        <$> (centi_fn (fToC faren))
    -- cToF & fToC as Converter-Functions defined someplace else

Hiermit kann man dann auch Funktionen, die auf Grad-Celsius rechnen auf Daten anwenden, die eigenlich nur Fahrenheit speichern, aber eine Umrechnung bereitstellen. Analog kann man auch einen Zeit-Datentypen definieren, der intern mit Sekunden rechnet (und somit garantiert frei von Fehlern wie -3 Minuten oder 37 Stunden ist)

## Non-Record Strukturen

Das ganze kann man auch parametrisieren und auf Non-Record-Strukturen anwenden. Beispielhaft an einer Map verdeutlicht:

    -- from Data.Lens.At
    at :: Ord k => k -> Lens' (Map k v) (Maybe v)

    -- oder identisch, wenn man die Lens' auflöst:
    at :: Ord k, forall f. Functor f => k -> (Maybe v -> f Maybe v) -> Map k v -> f Map k v

    at k mb_fn m
      = wrap <$> (mb_fn mv)
      where
        mv = Map.lookup k m

        wrap :: Maybe v -> Map k v
        wrap (Just v') = Map.insert k v' m
        wrap Nothing   = case mv of
                           Nothing -> m
                           Just _  -> Map.delete k m

    -- mb_fn :: Maybe v -> f Maybe v

## Weitere Beispiele

- Bitfields auf Strukturen die Bits haben (Ints, …) in Data.Bits.Lens

- Web-scraper in Package hexpat-lens

      p ^.. _HTML' . to allNodes
                   . traverse . named "a"
                   . traverse . ix "href"
                   . filtered isLocal
                   . to trimSpaces

  Zieht alle externen Links aus dem gegebenen HTML-Code in p um weitere ziele fürs crawlen zu finden.

## Erweiterungen

Bisher hatten wir Lenses nur auf Funktoren F. Die nächstmächtigere Klasse ist Applicative.

    type Traversal' s a = forall f. Applicative f
                                 => (a -> f a) -> (s -> f s)

Da wir den Container identisch lassen (weder s noch a wurde angefasst) muss sich etwas anderes ändern. Statt eines einzelnen Focus erhalten wir viele Foci.

Was ist ein Applicative überhaupt? Eine schwächere Monade (nur 1x Anwendung und kein Bind - dafür kann man die beliebig oft hintereinanderhängen).

    class Functor f => Applicative f where
      pure  :: a -> f a
      (<*>) :: f (a -> b) -> f a -> f b

    -- Monade als Applicative:
    pure = return
    mf <*> mx = do { f <- mf; x <- mx; return (f x) }

Recap: Was macht eine Lens:

    data Adress = A { _road :: String
                    , _city :: String
                    , _postcode :: String }

    road :: Lens' Adress String
    road elt_fn (A r c p) = (\r' -> A r' c p) <$> (elt_fn r)
    --                      |    "Hole"     |     | Thing to put in|

Wenn man nun road & city gleichzeitig bearbeiten will:

    addr_strs :: Traversal' Address String
    addr_strs elt_fn (A r c p)
      = ... (\r' c' -> A r' c' p)    .. (elt_fn r) .. (elt_fn c) ..
    --      | function with 2 "Holes"|  first Thing |  second Thing

fmap kann nur 1 Loch stopfen, aber nicht mit n Löchern umgehen. Applicative mit <*> kann das.
Somit gibt sich

    addr_strs :: Traversal' Address String
    addr_strs elt_fn (A r c p)
      = pure           (\r' c' -> A r' c' p)  <*> (elt_fn r) <*> (elt_fn c)
    --  lift in Appl. | function with 2 "Holes"|  first Thing |  second Thing
    -- oder kürzer
    addr_strs :: Traversal' Address String
    addr_strs elt_fn (A r c p)
      = (\r' c' -> A r' c' p)  <$> (elt_fn r) <*> (elt_fn c)
    -- pure x <*> y == x <$> y

Wie würd eine modify-funktion aussehen?

    over :: Lens' s a -> (a -> a) -> s -> s
    over ln f = runIdentity . ln (Identity . f)

    over :: Traversal' s a -> (a -> a) -> s -> s
    over ln f = runIdentity . ln (Identity . f)

Der Code ist derselbe - nur der Typ ist generischer. Auch die anderen Dinge funktioniert diese Erweiterung (für Identity und Const muss man noch ein paar dummy-Instanzen schreiben um sie von Functor auf Applicative oder Monad zu heben

- konkret reicht hier die Instanzierung von Monoid). In der Lens-Library ist daher meist Monad m statt Functor f gefordert.

## Wozu dienen die Erweiterungen?

Man kann mit Foci sehr selektiv vorgehen. Auch kann man diese durch Funktionen steuern. Beispisweise eine Funktion anwenden auf

- Jedes 2. Listenelement
- Alle graden Elemente in einem Baum
- Alle Namen in einer Tabelle, deren Gehalt > 10.000€ ist

Traversals und Lenses kann man trivial kombinieren (`lens . lens` => `lens`, `lens . traversal` => `traversal` etc.)

## Wie es in Lens wirklich aussieht

In diesem Artikel wurde nur auf Monomorphic Lenses eingegangen. In der richtigen Library ist eine Lens

    type Lens' s a = Lens s s a a
    type Lens s t a b = forall f. Functor f => (a -> f b) -> (s -> f t)

sodass sich auch die Typen ändern können um z.B. automatisch einen Konvertierten (sicheren) Typen aus einer unsicheren Datenstruktur zu geben.

Die modify-Funktion over ist auch

    > over :: Profunctor p => Setting p s t a b -> p a b -> s -> t

> Edward is deeply in thrall to abstractionitis - Simon Peyton Jones

Lens alleine definiert 39 newtypes, 34 data-types und 194 Typsynonyme…
Ausschnitt

    -- traverseOf :: Functor f => Iso s t a b           -> (a -> f b) -> s -> f t
    -- traverseOf :: Functor f => Lens s t a b          -> (a -> f b) -> s -> f t
    -- traverseOf :: Applicative f => Traversal s t a b -> (a -> f b) -> s -> f t

    traverseOf :: Over p f s t a b -> p a (f b) -> s -> f t

dafuq?

Links to this page

- Talks und Posts zu Haskell
  Lenses (Registrierung nötig - kostenfrei), siehe auch: Lenses
- Fortgeschrittene funktionale Programmierung in Haskell
  Umschreiben von explizitem Argument-Passing hin zu Monad-Transformers mit stateful lenses