Testing for map membership with lenses

Viewed 115

What is the idiomatic way of using lenses to check if a stateful map has a key? Here is my current attempt:

module Foo where

import Control.Lens
import Data.Map
import Control.Monad.State
import Data.Maybe (isJust)

check :: Int -> StateT (Map Int Int) IO ()
check k = do
  present <- use $ at k.to isJust
  unless present $ lift $ putStrLn "Not present!"

This works, but the to isJust part feels a bit clunky...

2 Answers

For this particular case, keep it simple and don't use lens:

present <- gets (member k)

If you're going to use lens anyway, e.g. you need to traverse into the state through some fields to get the map, use uses:

present <- uses (field1.field2) (member k)

To write the first action in terms of uses, use the identity optic id:

present <- uses id (member k)

but I would not recommend doing so gratuitously.

I'd only add that seeing if a key is in a map == checking if the traversal on that key has a target.

has (ix 5) :: (Ixed s, Num (Index s)) => s -> Bool

use (to $ has $ ix 5) :: 
  (MonadState s m, Ixed s, Num (Index s)) => m Bool

uses id (has $ ix 5) :: 
  (MonadState s m, Ixed s, Num (Index s)) => m Bool

But it's clear none of these are the perfectly appropriate lens. Looking at the source code,

use = gets . view

So we could instead write,

hasUse :: (MonadState s m) => Getting Any s a -> m Bool
hasUse = gets . has

:t hasUse $ ix 5
hasUse $ ix 5 :: 
  (MonadState s m, Ixed s, Num (Index s)) => m Bool

Another way that I found illustrative,

Any present <- uses (ix 5) (const $ Any True)
Related