I have a list of records and need a function which searches the list for a record with a given name and modify the value of this record OR if no record matches append a new record to the resulting list. Here is my code so far:
import Control.Lens
import Control.Applicative ((<$>), pure)
import Data.List (any)
data SomeRec = SomeRec { _name :: String, _val :: Int }
$(makeLenses ''SomeRec)
_find :: (a -> Bool) -> Simple Traversal [a] a
_find _ _ [] = pure []
_find pred f (a:as) = if pred a
then (: as) <$> f a
else (a:) <$> (_find pred f as)
changeOrCreate :: [SomeRec] -> String -> (Int -> Int) -> [SomeRec]
changeOrCreate recs nameToSearch valModifier =
if (any (\r -> r^.name == nameToSearch) recs)
then over (_find (\r -> r^.name == nameToSearch)) (over val valModifier) recs
else recs ++ [SomeRec nameToSearch (valModifier 0)]
It works fine, but I'm wondering if there is a more direct way of writing this using Data.Lens
(without the if
-construct)? Also, do I have to write the _find
function or is there something equivalent in the library?
Update: Here is a Gist of the source to experiment: https://gist.github.com/SKoschnicke/5795863
How about:
changeOrCreate :: String -> (Int -> Int) -> [SomeRec] -> [SomeRec]
changeOrCreate nameToSearch valModifier =
pos . val %~ valModifier
& outside (filtered (not . has pos)) %~ (. newRec)
where
pos :: Traversal' [SomeRec] SomeRec
pos = taking 1 (traversed . filtered (anyOf name (== nameToSearch)))
newRec = (SomeRec nameToSearch 0 :)
I don't know, but you can write some like
changeOrCreate [] n f = [SomeRec n (f 0)]
changeOrCreate (r:rs) n f | r^.name == n = (over val f) r:rs
| otherwise = r: changeOrCreate rs n f
So, _find
is not actually a Traversal
:
> [1..10] & over (_find odd) succ . over (_find odd) succ
[2,2,4,4,5,6,7,8,9,10]
> [1..10] & over (_find odd) (succ . succ)
[3,2,3,4,5,6,7,8,9,10]
That's the same sense filtered
is not a traversal.
Getting part can be mimicked with filtered
(it's okay here since Fold
does not have any laws):
> [1..10] ^? _find even
Just 2
> [1..10] ^? _find (> 20)
Nothing
> [1..10] ^? folded . filtered even
Just 2
> [1..10] ^? folded . filtered (> 20)
Nothing
Now, assuming "more direct way" is some clever Traversal
: no, that's not possible, Traversal
s can not modify the structure of the traversed thing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With