Safe Haskell | Safe-Infered |
---|
This modules provides a convienient way to access and update the elements of a structure.
It is very similar to Data.Accessors, but a bit more generic and has fewer dependencies.
I particularly like how cleanly it handles nested structures in state monads.
runSTLense
is also a very useful function.
A brief tutorial to get started:
To create a lense, you can use fromGetSet (although usually you would just derive them using templat haskell and deriveLenses
from Data.Lenses.Template):
lense = fromGetSet getField setField
The lense has type:
lense :: (MonadState r m) => StateT a m b -> m b
Where r is the type of the record, and a is the type of the field, (b can be any type you choose, more on that latter). Though it may help to think of it as:
lense :: State a b -> State r b
Which is not entirely accurate, but emphasises how the lense works. You can think of it as "pass in an action that operates on the field, and you get an action that operates on the record". So say we pass in get (with a more specific type for clarity):
get :: State a a lense get :: State r a
We get out a state monad that we can run on our record to fetch our field.
fieldValue = lense get `evalState` record
This module has a special function fetch
that does this:
fieldValue = record `fetch` lense
You can also pass in put
to get back an action that updates the field.
put :: a -> State a () lense (put someValue) :: State r ()
Now we have a state monad that we can run on our record to update our field.
updatedRecord = lense (put someValue) `execState` record
This module has a special function update
that does this:
updatedRecord = (record `update` lense) someValue
To aid in clarity and to deal with the actual types of the lenses this module provides execIn
, evalFrom
, and runOn
to be used in place of execState
, evalState
, and runState
. Also note that execIn
, evalFrom
, and runOn
have their parameters fliped from their state counterparts. There is nothing magical about these functions, they are just a little more handy than their state counterparts.
The lenses are especially convienient if you have nested structures. Lense composition is just function composition.
data Point = Point { x_ :: Float, y_ :: Float } deriving (Show)
$( deriveLenses ''Point )
data Triangle = Triangle { pa_ :: Point, pb_ :: Point, pc_ :: Point } deriving (Show)
$( deriveLenses ''Triangle )
a_y :: (MonadState Triangle m) => StateT Float (StateT Point m) b -> m b a_y = pa . y
a_y is now a lense that can operate on the y coordinate of point "a" inside a triangle. We can use a_y to fetch the coordinate or update it, on whatever triangle we choose.
someTriangle = Triangle (Point 5 3) (Point 0 1) (Point 10 6) ayValue = someTriangle `fetch` a_y -- ayValue == 3 updatedTriangle = (someTriangle `update` a_y) 7 -- updatedTriangle == Triangle (Point 5 7) (Point 0 1) (Point 10 6)
Or we could apply our lense to an action and pass it into execIn
(someTriangle `update` a_y) 7 == execIn someTriangle (a_y (put 7))
We can also chain actions together:
a_x :: (MonadState Triangle m) => StateT Float (StateT Point m) b -> m b a_x = pa . x c_y :: (MonadState Triangle m) => StateT Float (StateT Point m) b -> m b c_y = pc . y updatedTriangle = execIn someTriangle $ a_y (put 7) >> a_x (put 1) >> c_y (put 9) -- updatedTriangle == Triangle (Point 1 7) (Point 0 1) (Point 10 9)
What if we wanted to put the value of c_y into a_x? Can do!
updatedTriangle = execIn someTriangle $ do cy <- c_y get a_x $ put cy -- updatedTriangle == Triangle (Point 6 3) (Point 0 1) (Point 10 6)
Or if the order really bugs you, you can use the $%
operator (taken from Data.Accessors.Basic, it really should be in a standard lib)
updatedTriangle = execIn someTriangle $ do cy <- get $% c_y put cy $% a_x -- updatedTriangle == Triangle (Point 6 3) (Point 0 1) (Point 10 6)
Or you can use the $=
operator:
updatedTriangle = execIn someTriangle $ do cy <- c_y get a_x $= cy
Or more concisely:
updatedTriangle = execIn someTriangle $ (c_y get >>= a_x . put)
Or say we want to put the value of c_y into a_x, but want to throw an error if c_y is zero. We can do that as well!
updatedTriangle :: Either String Triangle updatedTriangle = execInT someTriangle $ do cy <- c_y get when (cy == 0) $ throwError "Something bad happend" a_x $ put cy -- updatedTriangle == Right $ Triangle (Point 6 3) (Point 0 1) (Point 10 6) -- if cy had equaled 0 then we would have gotten this: -- updatedTriangle == Left "Something bad happend"
Note that execInT
= flip
execStateT
.
Yay for monad transformers!
This module has one last feature that allows you to convert a function that fetches data from a structure to a function that modifies it! For an example see the documentation for runSTLense
.
One final note: Due to the generality of the lenses you might end up accidentally running into the monomorphism restriction. So if get a type error like:
Couldn't match expected type `SomeMonad SomeStructureType' against inferred type `Control.Monad.Identity.Identity SomeStructureType'
and nothing appears to be wrong with your code, try turning the restriction off with -XNoMonomorphismRestriction and see if it goes away. If it does then you probably need to add some explicit type signatures somewhere.
I whipped out this documentation in a hurry, so if you spot any errors, or think I should explain something better, please let me know. This library is hosted on github (click on the Contents link above and you should see the Homepage link) so it should be very easy to forked it and send patches to me. Also since this module is new I'm open to radical modifications if you have a good suggestion, so suggest away! :)
- fromGetSet :: MonadState r m => (r -> a) -> (a -> r -> r) -> StateT a m b -> m b
- fetch :: MonadState a m => r -> (m a -> StateT r Identity a) -> a
- update :: MonadState a m => r -> (m () -> StateT r Identity b) -> a -> r
- alter :: MonadState a m => (m () -> StateT r Identity b) -> (a -> a) -> r -> r
- runOn :: b -> StateT b Identity a -> (a, b)
- runOnT :: b -> StateT b m a -> m (a, b)
- evalFrom :: b -> StateT b Identity a -> a
- evalFromT :: Monad m => b -> StateT b m a -> m a
- execIn :: a -> StateT a Identity b -> a
- execInT :: Monad m => b -> StateT b m a -> m b
- runSTLense :: (Traversable f, Traversable t) => (forall s. f (State a b -> s) -> t s) -> f a -> (t b, f a)
- to :: Functor f => a -> (c -> f (a -> b)) -> c -> f b
- from :: (Traversable t, Traversable f) => (forall s. t (State a b -> s) -> f s) -> t a -> t a
- getAndModify :: MonadState s m => (s -> s) -> m s
- modifyAndGet :: MonadState s m => (s -> s) -> m s
- ($=) :: MonadState s m => (m () -> b) -> s -> b
- ($%) :: a -> (a -> b) -> b
Basic functions to create lenses and use them
fromGetSet :: MonadState r m => (r -> a) -> (a -> r -> r) -> StateT a m b -> m bSource
This function takes a getter and setter function and returns our lense.
Usually you only need to use this if you don't want to use Template Haskell to derive your Lenses for you. With a structure Point:
data Point = Point { x_ :: Float, y_ :: Float } deriving (Show)
This (from Data.Lenses.Template):
$( deriveLenses ''Point )
is equivalent to this:
x :: (MonadState Point m) => StateT Float m b -> m b x = fromGetSet x_ (\a s -> s { x_ = a }) y :: (MonadState Point m) => StateT Float m b -> m b y = fromGetSet y_ (\a s -> s { y_ = a })
fetch :: MonadState a m => r -> (m a -> StateT r Identity a) -> aSource
fetches a field from a structure using a lense:
somePoint = Point 5 3 a = somePoint `fetch` x b = somePoint `fetch` y
-- a == 5 -- b == 3
update :: MonadState a m => r -> (m () -> StateT r Identity b) -> a -> rSource
updates a field in a structure using a lense:
somePoint = Point 5 3 newPoint = (somePoint `update` y) 15
-- newPoint == Point 5 15
alter :: MonadState a m => (m () -> StateT r Identity b) -> (a -> a) -> r -> rSource
alters a field in a structure using a lense and a function:
somePoint = Point 5 3 newPoint = (somePoint `alter` y) (+1)
-- newPoint == Point 5 4
Lense evaluators
runOn :: b -> StateT b Identity a -> (a, b)Source
Runs a state monad action on a structure and returns the value returned from the action and the updated structure.
somePoint = Point 5 3 a = runOn somePoint $ x (modifyAndGet (+1)) -- a == (6, Point 6 3)
evalFrom :: b -> StateT b Identity a -> aSource
Runs a state monad action on a structure and returns the value returned from the action.
Use it to fetch values from fields.
someTriangle = Triangle (Point 5 3) (Point 0 1) (Point 10 6) a = evalFrom someTriangle $ pb . x get -- a == 0
note that:
evalFrom someTriangle (pb . x get) == someTriangle `fetch` (pb . x)
The advantage over fetch
is that it allows you to specify a different final action besides get
like so:
evalFrom someTriangle $ pb . x (modifyAndGet (+1))
evalFromT :: Monad m => b -> StateT b m a -> m aSource
Monad transformer version of evalFrom
. Note that evalFromT
= flip
evalStateT
.
execIn :: a -> StateT a Identity b -> aSource
Runs a state monad action on a structure and returns the updated structure
Use it to update fields:
somePoint = Point 5 3 a = execIn somePoint $ x (put 1) -- a == Point 1 3
note that:
execIn somePoint (x (put 1)) == (somePoint `update` x) 1
The advantage over update
is that it allows you to specify a different final action besides put
like so:
a = execIn somePoint $ x (modifyAndGet (+1)) -- a = Point 6 3
execInT :: Monad m => b -> StateT b m a -> m bSource
Monad transformer version of execIn
. Note that execIn
= flip
execStateT
.
Structure lenses
runSTLense :: (Traversable f, Traversable t) => (forall s. f (State a b -> s) -> t s) -> f a -> (t b, f a)Source
This function has the magical ability to convert a function that fetches elements from a structure, to a function that lets you modify the elements in the structure.
The catch is that the structure must be a member of Traversable
.
So say you have a function that gets the diagonal of a list of lists:
diagonal :: [[a]] -> [a]
we can make a function that increments the diagonal like so:
addOne :: State Int () addOne = modify (+1)
incrementDiagonal :: [[a]] -> [[a]] incrementDiagonal xss = snd $ runSTLense (fmap ($ addOne) . diagonal) xss
Of course there are some helper combinators to make this cleaner:
incrementDiagonal xss = (addOne `to` diagonal) `from` xss
runSTLense
takes a function, a traversable structure, and returns a pair of (collected values, updated structure)
For clarification:
specialFunction :: (Traversable f, Traversable t) => f (State a b -> s) -> t s (collectedValues, updatedStructure) = runSTLense specialFunction originalStructure collectedAlmostValues = specialFunction processedOriginalStructure
processedOriginalStructure has the same shape as originalStructure but every element has been replaced with a transformer function (State a b -> s). specialFunction needs to return the result of the application of the functions in processedOriginalStructure to a state monad. The state monad by definition will return a result and potentially update state. Getting state will get the value of the element in originalStructure. Updating state will update the value of the element in updatedStructure. The returned values are gathered in collectedValues.
to :: Functor f => a -> (c -> f (a -> b)) -> c -> f bSource
A helper combinator used for applying a monad to element collected by a fetching function.
For example:
everyOther :: [a] -> [a] everyOther [] = [] everyOther (x:[]) = [x] everyOther (x:y:xs) = x : everyOther xs
addOne :: State Int () addOne = modify (+1)
test :: [Int] test = (addOne `to` everyOther) `from` [1, 2, 9, 6, 7, 8, 4] -- test == [2, 2, 10, 6, 8, 8, 5]
which is the same as:
test = snd $ runSTLense (addOne `to` everyOther) [1, 2, 9, 6, 7, 8, 4]
which is the same as:
test = snd $ runSTLense (fmap ($ addOne) . everyOther) [1, 2, 9, 6, 7, 8, 4]
from :: (Traversable t, Traversable f) => (forall s. t (State a b -> s) -> f s) -> t a -> t aSource
Applies runSTLense
to a function and a structure and returns the snd
of the result.
See to
for example of use.
Generic helper functions
getAndModify :: MonadState s m => (s -> s) -> m sSource
Modifies the state in a state monad and returns the original value.
getAndModify
and modifyAndGet
should really be in Class
modifyAndGet :: MonadState s m => (s -> s) -> m sSource
Modifies the state in a state monad and returns the new value.
($=) :: MonadState s m => (m () -> b) -> s -> bSource
An operator for assigning a value to the value referenced by a lense. (see the example near the end of the tutorial at the start of this module)