Modifying a random String stored in the clipboard

I wrote a simple program, that will make a string 'noisy' if the clipboard contains one. What disappoints me, is that I should manually check what I got from getClipboardString — in the Nothing case, we simply return from program, otherwise we modify the string. Is there a better way to do this kind of check?

import Data.Char (toUpper)
import System.Random (randomIO)
import System.Clipboard (setClipboardString, getClipboardString)

main :: IO ()
main = do
join $fmap (test doNoise) getClipboardString where test :: (String -> IO ()) -> (Maybe String) -> IO () test _ Nothing = return () test f (Just s) = f s doNoise :: String -> IO () doNoise s = do capsed <- (sequence$ map randCap s)
setClipboardString capsed

randCap :: Char -> IO Char
randCap x = fmap ($x)$ fmap choice (randomIO :: IO Bool)

choice :: Bool -> (Char -> Char)
choice x = if x then toUpper else id


main :: IO ()
randCap x = bool id toUpper <$> (randomIO :: IO Bool) <*> pure x  Edit: Each change was arrived at through pattern-matching from my experience; compact code is usually easier to work with further. Looking at this again makes me think there ought to be a more lensy solution, one that fuses getting and setting, and indeed: main = modifyClipboardString =<< zipWith (bool id toUpper) <$> getRandoms

We are fortunate to not actually need side effects depending on clipboard contents, but if we did, I would recommend writing a monadic variant of modifyClipboardString.
• Some thoughts that I have: first, that I should use hoogle more thoroughly (I didn't know of traverse, traverse_ and bool); second, that I should read my code more closely, because I missed join \$ fmap == (=<<). Anyway, elegant solution, will upvote later, not enough rep right now – Stuthedian Jun 12 '19 at 5:59