Question

In yet another attempt to teach myself ekmett's lens library, I'm modifying a simple text-based hangman game to use lenses instead of record updates. The code itself works, but I've come across a place where I feel like I'm using the lens library incorrectly. This code works correctly, but it just doesn't look as elegant to me as all the lens tutorials I've seen.

First, my types:

data GameState = GameState
    { _guess :: Set Char
    , _secret :: String
    , _wrongGuesses :: Int
    } deriving (Eq, Show)

makeLenses ''GameState

type Game a = StateT GameState IO a

The function I'm bothered by is where I process the guess obtained from the user:

nextGuess :: Game ()
nextGuess = do
    gs <- get  -- GameState
    g <- getUserGuess :: Game Char
    -- If the user guessed a letter that wasn't in the secret word
    when (g `notElem` gs^.secret) $
        -- increment the number of wrong guesses
        wrongGuesses += 1
    -- Insert that guess into the set of guesses
    guess .= gs^.guess.to (insert g)

I'm mostly fine with the line

when (g `notElem` gs^.secret) $ wrongGuesses += 1

But the next line, where the guess is inserted into the current set of guesses seems like a roundabout way to do it. Having to get the current set, insert, then set it again just doesn't sound like the "lens way of doing things", especially when the line before all I have to do to increment the wrongGuesses counter is use += 1.

Is there a better way to do this?

Was it helpful?

Solution

Unless I'm much mistaken,

guess %= insert g

(%=) acts as modify and will feed the Set into insert g and then stick it back into guess.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top