Magnifying Lenses

Posted on May 22, 2016
Tags: haskell, lenses, state, monads

Learning about State with Lenses!

Let’s play with some neato Haskell things: the State Monad and a basic application of Lenses !

Level of Difficulty

Probably best for intermediate level Haskellers. Definitely useful to understand anonymous functions, Records, Typeclasses, and be at least somewhat familiar with the monad typeclass. It is also very useful to be able to install the lens library, either with cabal, stack, or your system’s package manager.

Difficulties and Motivation

The State Monad, in particular, has been a fairly difficult concept for me to get a handle on. This has been for a couple of reasons, I think.

First, the implementation of bind (>>=) is a whole lotta strange, especially in very concise Haskell.

Second, the example code available all seems very abstract. Even in one of my favourite State monad examples, the mechanics are exposed somewhat (simple uses of get and put), but the reason one would want to use the State monad are not obvious. There’s a wonderful elucidation of bind, but no desugared examples of bind. (I believe the get and put operations are the kinds of operations in need of bind, but that need is usually obscured with do-notation.

Third, there is no State constructor that is exported for the user. Why is this a problem? Unfortunately many examples of State monads seem to have been written at a time when the language did export one, so trying to run the code in the examples doesn’t actually work. In modern Haskell, State is actually implemented with a monad transformer stack, so getting your computation into the State context is a bit different than:

foo :: State Int [Int] foo = State $ \(x:xs) -> (x,xs)

N.B. The above function ‘foo’ will not actually compile in GHC 7.10.2.

Language Pragmas and the Import Declaration

We need two language pragmas. Template Haskell is a metaprogramming facility that can be used to generate lenses. RankNTypes seems to help with using the lenses (due to the positioning). More information can be found at this Stack Overflow question.

> {-# LANGUAGE TemplateHaskell, RankNTypes #-}

To access all the functions we’ll need, import these modules:

> import Control.Lens
> import Control.Monad.State

Introducing our Heroine!

Below, we have the data declaration for our Heroine record. Lots of useful information is stored here about a Heroine. Most of the fields are Ints (to which I have added strictness annotations to improve efficiency. We shall see if it works or even matters.).

> data Heroine = Heroine { _name    :: String
>                        , _age     :: !Int
>                        , _hp      :: !Int
>                        , _str     :: !Int
>                        , _dex     :: !Int
>                        , _wisdom  :: !Int
>                        , _int     :: !Int
>                        , _con     :: !Int
>                        } deriving (Eq, Show)

A sample instantiation of the Heroine record, starring yours truly. All the underscores sure are strange looking, but there is a good reason. We will explore that when we start talking about lenses. For now, just accept that thems the field names.

> rianna :: Heroine
> rianna = Heroine { _name = "Rianna"
>                  , _age = 479
>                  , _hp = 6
>                  , _str = 6
>                  , _dex = 10
>                  , _wisdom = 10
>                  , _int = 15
>                  , _con = 11
>                  }

Modifying Records

Suppose that our Heroine increases her hit points. How exactly should one go about updating the record? In Haskell, this is a somewhat involved process. Consider the following code:

> hpMod :: Int -> Heroine -> Heroine
> hpMod n char = char {_hp = new} where new = old + n
>                                       old = _hp char

hpMod takes an Int and a Heroine and returns a Heroine. The Int is the increase or decrease in hp and the Heroine is the character to whom this is happened. I think it is obvious why this is a pain. We have to reference the record’s accessor function no less than twice – once to set the new value and once to calculate retrieve the old value necessary to get the new one.

Even more concisely:

> hpMod' :: Int -> Heroine -> Heroine
> hpMod' n char = char {_hp = (_hp char) + n}

I personally like to use where clauses to stuff some of the lower level implementation details so the reader can focus on the semantics of what is actually happening.

Since records are such a pain to update, what do we do? My first inclination was to try and write a function to take any accessor function and apply to the record. This kinda works, but I ran into a problem: You can’t really treat the field names as variable. For instance, one cannot wite

> modStat :: (Heroine -> Int) -> Heroine -> Heroine
> modStat f n char = char {f = (f char) + n }

Why won’t this work? Because the ‘f’ is not actually a field defined in our Heroine type. The compiler does not replace the first ‘f’ in the body of the function definition with the name of the accessor function. I thought this was a great idea when I thought of it, but quickly ran into this issue. One could write a bunch of case handling statements, if one wrote modification functions for field in the record, but that seemed like a whole lot of boilerplate.

Fortunately for me, I’ve been around Haskell long enough to know what tool to use to address this problem: the lens library.

Magnifying Lenses!!!

The lens library is super useful for accessing and modifying data structures like records, tuples, or other sorta nested structures. Lenses obviate the need for the repetitious setting processes dísplayed in hpMod and hpMod’.

Lenses have some notoriety for being difficult to use. I think this may be due to the strange type signatures, in part at any rate.I came across this disheartening comic a couple of years ago and felt quite worried.

Contrary to that comic, with about an hour of reading and playing with some code, I figured out how to get lenses to do the work I needed done. This may not hold for everyone, but I’d like to provide some very basic, working code using lenses, without worrying too much about the gnarly type signatures.

In order to do this, though, we’ll need to work with a brief snippet of Template Haskell for making the lenses for the Heroine type. The following line does just this:

> makeLenses ''Heroine

When this file is loaded into GHCI, there should be functions such as “name”, “hp”, “wisdom”, etc. available. Remember where the Heroine type was declared with leading underscores for the accessor functions? Those underscores help Template Haskell work its magic to generate the lenses! Yay!

Now that the lenses have been generated, let’s do some things with our Heroine. All her diligent study of Lenses has brought wisdom! We can view her wisdom in the following way:

> checkWis :: Heroine -> Int
> checkWis = view wisdom    -- Point-free

Now, though, we need to modify this value? We can use set to achieve this. We pass n and char to set wisdom. Because of currying, set wisdom is just a partially applied function, which needs the rest of the arguments added. We’ll say that our Heroine has achieved a whole point of wisdom. Using this neato wisdom setting function, we can set our heroine’s wisdom to whatever we want:

> setWis :: Int -> Heroine -> Heroine
> setWis n char = set wisdom n char

setWis 11 rianna

However, there is an obvious problem: With this method, one needs to know what the Heroine’s wisdom is before to increment it. Fortunately, the lens library provides the over function. Over allows the programmer to apply a function to a value in a structure.

> incWis :: Heroine -> Heroine
> incWis = over wisdom (+1)

This particular function is really useful for when a character encounters a magical item that really ups her wisdom! In this case, our heroine hit the jackpot in a an abandoned library. Very magical tomes there. The one she found had not merely an additive effect, but a multiplicative effect!

wisMod does not currently (starling electric – no clear winner) illustrate the point i wanted to make with over versus set.

> wisMod :: Double -> Heroine -> Heroine
> wisMod n char = set wisdom new char where new    = round (n * oldwis)
>                                           oldwis = fromIntegral $ view wisdom char
> hpMod2 :: Int -> CharState
> hpMod2 n = state $ \s -> ((), s {_hp = (_hp s) + n})

Could write it as: hpMod2 :: Int -> Heroine -> ((), Heroine)

Introducing State!

Why the heck does one need state for?? Basically, it’s a trick to keep a lazy functional language totally pure, but still simulate stateful computations. It seems useful for things like randon number generation and character information in games (or another stateful part of a game). I’m sure there are many more examples.

Let’s take a look at a simplified state definition:

> newtype State s a = State { runState :: (s -> (a,s)) }

State s a is a wrapper around a record type that contains a function called runState. runState takes a state ‘s’ and returns a new value and a new state (a, s).

We need to define our own state for modifying our Heroine’s stats.

> type CharState = State Heroine ()

It seems like this needs the unit type because put doesn’t really have a result, so much as it is an action. The result is returns is unit (()), but it does change something that would in an imperative language, normally be out of the functions scope.

LYAH type synonym

> type Stack = [Int]
> initialState = rianna
> wisMod :: Int -> CharState
> wisMod n = state $ \s -> ((), s {_wisdom = (_wisdom s) + n})

Here is a modified version of wisMod that uses lenses to modify the field. Much nicer than typing in all the all the boiler plate in wisMod.

> wisMod' :: Int -> CharState
> wisMod' n = state $ \s -> ((), over wisdom (+ n) s)

A function to modify any stat in a Heroine structure.

> anyMod' :: Lens' Heroine Int -> Int -> CharState
> anyMod' l n = state $ \s -> ((), over l (+ n) s)
> strMod :: Int -> CharState
> strMod n = state $ \s -> ((), s {_str = (_str s) + n})
> birthday :: CharState
> birthday = state $ \s -> ((), s {_age = (_age s) + 1})

LYAH borrowed code

> pop :: State Stack Int pop = state \$ (x:xs) -> (x,xs)
> push :: Int -> State Stack () push a = state $ \xs -> ((),a:xs)