Reader small image

You're reading from  Soar with Haskell

Product typeBook
Published inDec 2023
Reading LevelBeginner
PublisherPackt
ISBN-139781805128458
Edition1st Edition
Languages
Right arrow
Author (1)
Tom Schrijvers
Tom Schrijvers
author image
Tom Schrijvers

Tom Schrijvers is a professor of computer science at KU Leuven in Belgium since 2014, and previously from 2011 until 2014 at Ghent University in Belgium. He has over 20 years of research experience in programming languages and has co-authored more than 100 scientific papers. Much of his research focuses on functional programming and on the Haskell programming language in particular: he has made many contributions to the language, its ecosystem and applications, and chaired academic events like the Haskell Symposium. At the same time, he has more than a decade of teaching experience (including functional programming with Haskell) and received several teaching awards.
Read more about Tom Schrijvers

Right arrow

Lenses

Data access in nested data types is one of the most common and mundane activities in real-world programs. In imperative and object-oriented programming languages, reaching deep inside nested objects or records is relatively frictionless. This is not the case in Haskell. Because data structures are immutable, updating a field means rebuilding the data structure around the new value for that field. This is especially painful when the field appears deeply nested inside the structure.

The concept of lenses was developed to remedy this problem. In its basic form, a lens is a data accessor that can be used to both inspect and modify the value of a field in a data structure. Unlike the typical data accessors built into programming languages, lenses are first-class: they can be composed, passed around, and used on different instances of the same data structure. Of course, this wouldn’t be Haskell if we didn’t take the idea to the next level. Indeed, lenses can perform...

Technical requirements

Support for lenses is not part of the Haskell standard library but needs to be installed as a separate package. Hackage features many lens packages. The most widely used and most feature-rich package is simply called lens. Its enormous size and dependency on many other packages is sometimes a reason not to use it. For that reason, and to keep things accessible, I recommend starting with a smaller package that only provides the essential functionality of the lens package: microlens. This is the package we will be using in this chapter.

Records and deep access

Before we dive into lenses, let’s review Haskell’s built-in support for data access and its shortcomings.

Haskell’s built-in support for data access is the named fields of record types that we first saw in Chapter 2, Algebraic Data Types. Here is a small example of this:

data Employee = MkEmployee { firstName :: String
                           , lastName  :: String
                           , salary    :: Int
                           }

This Employee type has one data constructor, MkEmployee, with three...

Lenses and their composition

Lenses are convenient abstractions for data access that are composed nicely. This way, they facilitate deep data access.

Basic lenses

In essence, we can think of a lens as a pair of two functions – one for retrieving a component of a larger data type and another for replacing that component with a new value:

data Lens' s v = MkLens' { view :: s -> v
                         , set  :: v -> s -> s }

The larger data type is sometimes called source. Here, this is indicated with the s type variable. The component that the lens focuses on is then called view, which is indicated by the v type variable here.

When using lenses, the convention is to prefix the regular field names with an underscore, and to the regular names themselves for the lenses. For example, instead of the previous definition...

Programmatic data access

Unlike the built-in field access functionality in programming languages, lenses are defined entirely programmatically. In the previous section, we used them rather rigidly to access actual fields of data types. Nothing forces those fields to be present, and we can deviate from them if we want.

Virtual fields

Consider the following data type for claiming car trip expenses:

data Trip = MkTrip { _origin       :: String
                   , _destination  :: String
                   , _distanceInKm :: Float }

The distance is stored in kilometers and comes with an appropriate lens:

distanceInKm :: Lens' Trip Float
distanceInKm t (MkTrip o d km) =
  fmap (\km' -> MkTrip o d km') (t km)

However...

Advanced lenses

One of the interesting insights of the function-based lens representation is that we can both generalize and specialize in various ways. These variations are often still compatible: they can be composed. We have already seen one example of this:

type Lens' s v
  = forall f. Functor f => (v -> f v) -> (s -> f s)
type Lens s t v w
  = forall f. Functor f => (v -> f w) -> (s -> f t)

The polymorphic lens is a generalization of the monomorphic one. When we have a monomorphic and a polymorphic lens of compatible types, they can be combined to yield a new monomorphic lens. For example, if we combine the _1 lens with the salary lens, we get a new lens that focuses on the salary of the employee in the first component of a tuple:

_1 . salary :: Lens' (Employee, b) Int

Another way in which specialization is possible is through the functor type parameter, f, in the lens definition.

Getters and setters

Earlier...

Summary

In this chapter, we looked at data access in deeply nested data structures. We saw how updating fields with the built-in record field functionality is awkward. As an alternative, we introduced lenses as first-class, easily composable data accessors. We saw that they not only allow purely functional updates of record fields but also support virtual fields and side effects. Moreover, besides focusing on one field, they can also focus on multiple fields or values at once.

Chapter 16, Property-Based Testing, concludes this book with another viral idea that was developed in the context of Haskell: property-based testing. It is a powerful alternative to ordinary unit testing. The idea is to not write unit tests manually but generate them automatically from a more high-level description – a property. From most properties, an unbounded number of unit tests can be automatically generated, either randomly or more systematically. This makes testing more thorough and fun.

...

Questions

Answer the following questions to test your knowledge of this chapter:

  1. What is a lens?
  2. What is a polymorphic lens?
  3. What is a traversal?

Further reading

To learn more about the topics that were covered in this chapter, take a look at the following resources:

Answers

Here are the answers to this chapter’s questions:

  1. A lens is a first-class data accessor. In its standard form, it focuses on a particular field in a data structure and allows you to both retrieve and modify the value of that field.

    Packages such as lens and microlens use the following function-based representation for (monomorphic) lenses:

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

    Here, the s type parameter denotes the source type (the data structure) and the v parameter denotes the view type (the field).

    A key property that makes working with lenses convenient is that they compose. The preceding function-based representation lens composition is simply function composition, (.).

  2. A polymorphic lens is a lens that allows you to modify the value of a view in a way that changes its type, from v to w. As a consequence, the type of the source changes as well, from s to t.

    Packages such as lens and microlens use the...

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Soar with Haskell
Published in: Dec 2023Publisher: PacktISBN-13: 9781805128458
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Tom Schrijvers

Tom Schrijvers is a professor of computer science at KU Leuven in Belgium since 2014, and previously from 2011 until 2014 at Ghent University in Belgium. He has over 20 years of research experience in programming languages and has co-authored more than 100 scientific papers. Much of his research focuses on functional programming and on the Haskell programming language in particular: he has made many contributions to the language, its ecosystem and applications, and chaired academic events like the Haskell Symposium. At the same time, he has more than a decade of teaching experience (including functional programming with Haskell) and received several teaching awards.
Read more about Tom Schrijvers