xal
xal

Reputation: 137

Using lenses for accessing data inside functors

I find lenses very useful for accessing deeply nested data, but frequently "containers" like MVar or TVars throw off some of the nice properties of lenses.

For example:

data SomeStruct = SomeStruct { _b :: Int
                             }
makeLenses ''SomeStruct

data AppState = AppState { _a :: SomeStruct
                         }
makeLenses ''AppState

data App = App { _state :: AppState
               }
makeLenses ''App

I can make new lenses using very nice left-to-right composition:

let v = App (AppState (SomeStruct 3))
in v^.state.a.b

However, if _state were of type TVar, the left-to-right composition breaks down, and lenses feel a lot clunkier to use:

t <- newTVarIO $ AppState (SomeStruct 3)
let v = App t
atomically $ (^.a.b) <$> readTVar (v^.state)

^.a.b gets pushed to the left hand side despite ^.state being the innermost lens. Is there some way I could deal with these sorts of "container" types and lenses in a more ergonomic way?

Upvotes: 4

Views: 191

Answers (1)

danidiaz
danidiaz

Reputation: 27771

There is a library (formerly part of lens proper) called lens-action that helps mixing getters and folds with monading actions without yanking you too much out of the lensy world.

For example, for the type

data App = App { _state :: TVar AppState }

We could write

ghci> :t \v -> v^!state.act readTVar.a.b
\v -> v^!state.act readTVar.a.b :: App -> STM Int

The idea is that instead of using the typical view function (^.) we use its monadic counterpart (^!). And we insert monadic actions using functions like act or acts. Normal getters and folds don't need to be lifted and composition is still done with plain (.).

Upvotes: 3

Related Questions