module Data.Lens.IxSet (ixLens) where
import Data.Data
import Data.IxSet
import Data.Lens.Common
{- |Focus on a key in an indexed set, much like with 'mapLens'.
Given an 'IxSet' of people:
@
people = 'fromList' [ Person (FirstName \"Edward A.\") (LastName \"Kmett\")
, Person (FirstName \"Simon\") (LastName \"P. Jones\")
]
@
We can now work with indices using lenses and fix Simon's last name:
@
people\' = 'ixLens' (FirstName \"Simon\") '^%=' 'fmap' (lastName '^=' LastName \"Peyton-Jones\") '$' people
@
>>> people'
fromList [Person {_firstName = FirstName "Edward A.", _lastName = LastName "Kmett"}
,Person {_firstName = FirstName "Simon", _lastName = LastName "Peyton-Jones"}]
>>> (firstName ^$) <$> people' ^. ixLens (LastName "Peyton-Jones")
Just (FirstName "Simon")
>>> ixLens (LastName "Peyton-Jones") ^$ people
Nothing
Perhaps more commonly you're working with an 'IxSet' from inside a state
monad such as @Update@ from the @acid-state@ package. In that case usage
is even easier:
@
changeLastName = 'ixLens' (FirstName \"Simon\") '%=' 'fmap' (lastName '^=' LastName \"Peyton-Jones\")
@
Here's the missing boilerplate, which also needs the packages @data-lens-fd@ and @data-lens-template@:
@
\{\-\# LANGUAGE DeriveDataTypeable \#\-\}
\{\-\# LANGUAGE TemplateHaskell \#\-\}
import Data.Data
import Data.IxSet
import Data.Lens
import Data.Lens.IxSet
import Data.Lens.Template
newtype FirstName = FirstName 'String'
deriving ('Show', 'Eq', 'Ord', 'Data', 'Typeable')
newtype LastName = LastName 'String'
deriving ('Show', 'Eq', 'Ord', 'Data', 'Typeable')
data Person = Person { _firstName :: FirstName
, _lastName :: LastName
} deriving ('Show', 'Eq', 'Ord', 'Data', 'Typeable')
makeLens ''Person
instance 'Indexable' Person where
empty = 'ixSet' [ 'ixGen' ('Proxy' :: 'Proxy' FirstName)
, 'ixGen' ('Proxy' :: 'Proxy' LastName)
]
@
-}
ixLens :: (Indexable a, Typeable a, Typeable k, Ord a)
=> k -> Lens (IxSet a) (Maybe a)
ixLens k = lens get set
where
get = getOne . getEQ k
set (Just v) = updateIx k v
set Nothing = deleteIx k