Modifying the target of a Lens conditionally
Asked Answered
G

1

6

I have a function that produces an updated board from an input and a board, if the move is permitted by the rules of the game:

move :: Input -> Board -> Maybe Board

The board is wrapped in the GameState type with some additional data:

type GameState = (Board, ...)

Now I'd like to use lenses to update the board in the game state if move yields Just a value. I can do this with a helper function:

updateGameState :: Input -> GameState -> GameState
updateGameState input gs = gs & _1 %~ (f $ move input)
  where
    f g x = maybe x id (g x)

However, I'm wondering if there is a combinator that modifies the target of a Lens only if the supplied function returns a Just. I've found the ?~ operator, but that handles partiality on the left hand side.

Is there a (likely more general) combinator that can achieve this, or is there another way to express this in a terse and idiomatic way?

Gumwood answered 16/1, 2015 at 19:23 Comment(0)
P
6

You could do

import Control.Applicative
import Data.Maybe

updateGameState :: Input -> GameState -> GameState
updateGameState input = fromMaybe <*> _1 (move input)
-- updateGameState x s = fromMaybe s $ _1 $ move x

This uses the fact that Lens s t a b is a type alias for forall f . Functor f => (a -> f b) -> (s -> f t), so we can choose to use Maybe as f and get a function of type GameState -> Maybe GameState.

Pacesetter answered 16/1, 2015 at 19:41 Comment(1)
Thanks. The Applicative instance of (->) r and this fromMaybe <*> f idiom is what I've been looking for.Gumwood

© 2022 - 2024 — McMap. All rights reserved.