# LGtk/ADT lenses

### From HaskellWiki

(refine description; add subsections) |
(fill subsection) |
||

Line 110: | Line 110: | ||

=== Example: ADT with repeated record fields === | === Example: ADT with repeated record fields === | ||

− | + | Consider the following ADT: | |

+ | |||

+ | <haskell> | ||

+ | data X a | ||

+ | = X1 { y :: Int, z :: a } | ||

+ | | X2 { y :: Int, v :: Char } | ||

+ | </haskell> | ||

+ | |||

+ | Note that the <hask>y</hask> field is defined twice. | ||

+ | |||

+ | For the ADT lens, first define an auxiliary enum type for the constructor tags: | ||

+ | |||

+ | <haskell> | ||

+ | data XTag = X1Tag | X2Tag | ||

+ | </haskell> | ||

+ | |||

+ | The definition of <hask>XTag</hask> is kind of inevitable if we would like to edit the constructor tags. We could use <hask>Bool</hask>, but that would not scale to more constructors. We could use <hask>Int</hask> too, but with less static checks from the type system. | ||

+ | |||

+ | The definition of the lens toolbox for <hask>X</hask>: | ||

+ | |||

+ | <haskell> | ||

+ | xLens :: Lens (XTag, (Int, (a, Char))) (X a) | ||

+ | xLens = lens get set where | ||

+ | |||

+ | get (X1Tag, (y, (z, _))) = X1 y z | ||

+ | get (X2Tag, (y, (_, v))) = X2 y v | ||

+ | |||

+ | set (X1 y z) (_, (_, (_, v))) = (X1Tag, (y, (z, v))) | ||

+ | set (X2 y v) (_, (_, (z, _))) = (X2Tag, (y, (z, v))) | ||

+ | </haskell> | ||

+ | |||

+ | Remarks: | ||

+ | |||

+ | * Instead of <hask>(XTag, (Int, (a, Char)))</hask>, we could use <hask>(XTag, (Int, a, Char))</hask> or <hask>(XTag, Int, a, Char)</hask> too. This is an implementation detail. | ||

+ | * <hask>xLens</hask> remembers the value of <hask>y</hask> if we change between the constructor tags. This is the intended behaviour. | ||

+ | * <hask>xLens</hask> remembers the values of <hask>v</hask> and <hask>z</hask> fields if we change between the constructor tags. This is the intended behaviour. | ||

=== Generic ADT lens === | === Generic ADT lens === |

## Revision as of 22:17, 7 June 2013

## Contents |

## 1 Problem description

Lenses provide uniform and compositional way to view and edit data structures.

For example, one can view and edit pairs withq == setL fstLens (getL fstLens q) (setL sndLens (getL sndLens q) p)

Similarly, there is a toolbox of lenses for records which toolbox contains one lens for each record field.

Are there a toolbox of lenses for algebraic data types with multiple constructors?

## 2 Existing solutions

### 2.1 Partial lenses

The data-lens library provides partial lenses which are isomorphic to

type PartialLens a b = (a -> Maybe b, a -> Maybe (b -> a))

The flollowing partial lenses are defined for lists:

headLens :: PartialLens [a] a headLens = (get, set) where get [] = Nothing get (h:t) = Just h set [] = Nothing set (h:t) = Just (:t)

tailLens :: PartialLens [a] [a] tailLens = (get, set) where get [] = Nothing get (h:t) = Just t set [] = Nothing set (h:t) = Just (h:)

### 2.2 Other solutions

*Please help to extend the list of known solutions.*

## 3 ADT lenses

The proposed solution, summarized:

**As a lens toolbox for an ADT, use a lens whose codomain is the ADT and whose domain is tuple of the constructor tag and the ADT components.**

Let's see specific examples before the generic descripton of the proposed lens.

### 3.1 Example: List lens

The lens for lists which forms a complete toolbox:

import Data.Lens.Common

listLens :: Lens (Bool, (a, [a])) [a] listLens = lens get set where get (False, _) = [] get (True, (l, r)) = l: r set [] (_, x) = (False, x) set (l: r) _ = (True, (l, r))

#### 3.1.1 List lens usage

Suppose that we have a statetype S = (Bool, (Int, [Int]))

We can view and edit the list through the following lenses:

- edits thelistLens :: Lens S [Int]
**complete list**. - edits thefstLens :: Lens S Bool
**top level constructor**of the list:corresponds toFalseand[]corresponds toTrue.(:) - edits theheadLens = fstLens . sndLens :: Lens S Int
**head**of the list. - edits thetailLens = sndLens . sndLens :: Lens S [Int]
**tail**of the list.

Remarks:

- If the top level constructor of the list is , the head and the tail of the list can still be edited; the change will only be visible through[]when the constructor is changed back tolistLens. This may seem to be odd, but for many applications this is the right behaviour.(:)
- For editing the tail of the tail of the list, we need an such thats' :: Sviewed throughsis the same astailLensviewed throughs'. Explained on a figure:listLens

*references*. One possible definition of references is described in LGtk/Semantics#References. How

### 3.2 Example: ADT with repeated record fields

Consider the following ADT:

data X a = X1 { y :: Int, z :: a } | X2 { y :: Int, v :: Char }

For the ADT lens, first define an auxiliary enum type for the constructor tags:

data XTag = X1Tag | X2Tag

xLens :: Lens (XTag, (Int, (a, Char))) (X a) xLens = lens get set where get (X1Tag, (y, (z, _))) = X1 y z get (X2Tag, (y, (_, v))) = X2 y v set (X1 y z) (_, (_, (_, v))) = (X1Tag, (y, (z, v))) set (X2 y v) (_, (_, (z, _))) = (X2Tag, (y, (z, v)))

Remarks:

- Instead of , we could use(XTag, (Int, (a, Char)))or(XTag, (Int, a, Char))too. This is an implementation detail.(XTag, Int, a, Char)
- remembers the value ofxLensif we change between the constructor tags. This is the intended behaviour.y
- remembers the values ofxLensandvfields if we change between the constructor tags. This is the intended behaviour.z

### 3.3 Generic ADT lens

TODO

## 4 Links and references

I have not seen this technique described before. Please help to extend the list of papers / blog entries, where this or similar technique is used.

[Reddit comments]