Isomorphism lenses

后端 未结 2 1917
长情又很酷
长情又很酷 2021-02-14 07:16

I would be interested in a small example of van Laarhoven\'s isomorphism lenses, applied to a data type like data BValue = BValue { π :: Float, σ :: Float, α :: Float } de

相关标签:
2条回答
  • 2021-02-14 07:56

    From van Laarhoven's post, the Lens type is

    data Lens a b = forall r . Lens (Iso a (b, r))
    

    So in our case a is BValue and we want to construct some leneses that pick out one or more of the elements. So for example, let's build a lens that picks out π.

    piLens :: Lens BValue Float
    

    So it's going to be a lens from a BValue to a Float (namely the first one in the triple, with label pi.)

    piLens = Lens (Iso {fw = piFwd, bw = piBwd})
    

    A lens picks out two things: a residual type r (omitted here because we don't have to explicitly specify an existential type in haskell), and an isomorphism. An isomorphism is in turn composed of a forward and a backward function.

    piFwd :: BValue ->  (Float, (Float, Float))
    piFwd (BValue {pi, sigma, alpha}) = (pi, (sigma, alpha))
    

    The forward function just isolates the component that we want. Note that my residual type here is the "rest of the value", namely a pair of the sigma and alpha floats.

    piBwd :: (Float, (Float, Float)) -> BValue
    piBwd (pi, (sigma, alpha)) = BValue { pi = pi, sigma = sigma, alpha = alpha }
    

    The backward function is analogous.

    So now we have defined a lens for manipulating the pi component of a BValue.

    The other seven lenses are similar. (7 lenses: pick out sigma and alpha, pick out all possible pairs (disregarding the order), pick out all of BValue and pick out ()).

    The one bit I'm not sure about is strictness: I am a little worried that the fw and bw functions I have written are too strict. Not sure.

    We are not done yet:

    We still need to check that piLens actually respects the lens laws. The nice thing about van Laarhoven's definition of Lens is that we only have to check the isomorphism laws; the lens laws follow via the calculation in his blog post.

    So our proof obligations are:

    1. fw piLens . bw piLens = id
    2. bw piLens . fw piLens = id

    Both proofs follow directly from the definitions of piFwd and piBwd and laws about composition.

    0 讨论(0)
  • 2021-02-14 08:16

    Check out Data.Label from the fclabels package, which implements lenses for record types.

    To illustrate this package, let's take the following two example datatypes.

    import Data.Label
    import Prelude hiding ((.), id)
    
    data Person = Person
     { _name   :: String
     , _age    :: Int
     , _isMale :: Bool
     , _place  :: Place
     }
    
    data Place = Place
     { _city
     , _country
     , _continent :: String
     }
    

    Both datatypes are record types with all the labels prefixed with an underscore. This underscore is an indication for our Template Haskell code to derive lenses for these fields. Deriving lenses can be done with this simple one-liner:

    $(mkLabels [''Person, ''Place])
    

    For all labels a lens will created.

    Now let's look at this example. This 71 year old fellow, my neighbour called Jan, didn't mind using him as an example:

    jan :: Person
    jan = Person "Jan" 71 True (Place "Utrecht" "The Netherlands" "Europe")
    

    When we want to be sure Jan is really as old as he claims we can use the get function to get the age out as an integer:

    hisAge :: Int
    hisAge = get age jan
    

    Consider he now wants to move to Amsterdam: what better place to spend your old days. Using composition we can change the city value deep inside the structure:

    moveToAmsterdam :: Person -> Person
    moveToAmsterdam = set (city . place) "Amsterdam"
    

    And now:

    ghci> moveToAmsterdam jan
    Person "Jan" 71 True (Place "Amsterdam" "The Netherlands" "Europe")
    

    Composition is done using the (.) operator which is part of the Control.Category module. Make sure to import this module and hide the default (.), id function from the Haskell Prelude.

    0 讨论(0)
提交回复
热议问题