Search code examples
haskellhaskell-lens

Haskell Lens: How to elegantly test list item


data Game = Game {
 _players :: [Player]
}

data Player = Player {
 _cards :: [Card]
}

data Card = Card {
 _status :: CardStatus
}

data CardStatus = Face | Back

Then I makeLenses on the above data types.

The game quit condition is that all the cards in a player's hand are Back. So in my Game StateT monad, I access all the CardStatus and test them. Since I am new to Lens I'm wondering what's the elegant way to write this. A little bit lost in the forest of Lens operators.


Solution

  • The module Control.Lens.Fold has many combinators for testing targets of lenses/folds/traversals: has (useful for checking that a prism matches), anyOf, noneOf, allOf...

    In your example (assuming we have also generated the prisms for CardStatus) we could do something like:

    endGame :: Game -> Bool
    endGame = anyOf (players.folded) (allOf (cards.folded.status) (has _Back))
    

    Also, to find which players have winning hands, we could use filtered:

    winners :: Fold Game Player
    winners = players.folded.filtered (allOf (cards.folded.status) (has _Back))
    

    These functions are analogous to typical list functions, but can be applied directly to folds, so they don't yank you out as much of the lensy world. For example, we could go on composing winners with another Fold.