Conditionally display an Action depending on whether or not a player is connected
It always looks a little odd when one has to 'invent' something that seems like it ought to be a common, built-in expression, so I can understand why you ask.

Please interpret any of the following as nothing but my humble attempt to share what I know about Haskell programming. I don't consider myself a Haskell expert, and I don't know if other people would consider any of the following idiomatic...

Simplify maybeWhen

Is it necessary to pass both a predicate and a value (a) when the value itself turns out to be a Boolean value?

Would it make sense to simplify maybeWhen to something like the following?

maybeWhen' :: Bool -&gt; Maybe b -&gt; Maybe b
maybeWhen' b mb =
  if b then mb else Nothing


This would enable you to rewrite displayConnectionAction like this:

displayConnectionAction :: Reader GameState (Maybe Action)
displayConnectionAction = maybeWhen' &lt;$&gt; (not &lt;$&gt; isConnected) &lt;*&gt; action
  where
    action =
      Just . Action "Connect" "/connect" &lt;$&gt; currentPlayerId

    isConnected =
      asks (playerConnected . currentPlayer)

    currentPlayerId =
      asks (playerId . currentPlayer)


All of the function body (from where and down) is the same; only the composition is different.

Notice that it first maps isConnected with not, and the applies the projected value and action to the simplified maybeWhen' function.

maybeWhen' is just a composition

Instead of writing a function like maybeWhen', you can compose it from bool from Data.Bool:

Prelude Data.Bool&gt; :t flip (bool Nothing)
flip (bool Nothing) :: Bool -&gt; Maybe a -&gt; Maybe a


Plugging that into displayConnectionAction means that you can now write it like this:

displayConnectionAction :: Reader GameState (Maybe Action)
displayConnectionAction = flip (bool Nothing) &lt;$&gt; isConnected &lt;*&gt; action
  where
    action =
      Just . Action "Connect" "/connect" &lt;$&gt; currentPlayerId

    isConnected =
      asks (playerConnected . currentPlayer)

    currentPlayerId =
      asks (playerId . currentPlayer)


Again, all code below where is the same. Notice, however, that you no longer need any helper function.

Or, instead of using the flip function, you could just flip the arguments:

displayConnectionAction :: Reader GameState (Maybe Action)
displayConnectionAction = bool Nothing &lt;$&gt; action &lt;*&gt; isConnected
  where
    action =
      Just . Action "Connect" "/connect" &lt;$&gt; currentPlayerId

    isConnected =
      asks (playerConnected . currentPlayer)

    currentPlayerId =
      asks (playerId . currentPlayer)


Notice that in this version, action and isConnected has changed places.

All this, however, relies on the simplification that in this particular example, the a in maybeWhen turns out to be Bool. Perhaps that's not always the case...

Use do-notation

Personally, I think this would be more readable if you rewrite the function using do notation. There are definitely Haskell programmers who consider do notation a crutch that has 'nothing to do with functional programming', but I don't share that sentiment.

Since Reader is a Monad, you could refactor the function to this implementation:

displayConnectionAction :: Reader GameState (Maybe Action)
displayConnectionAction = do
  currentPlayerId &lt;- asks (playerId . currentPlayer)
  let action = Just . Action "Connect" "/connect" $ currentPlayerId
  isConnected &lt;- asks (playerConnected . currentPlayer)
  if not isConnected
    then return action
    else return Nothing


Instead of having various Reader GameState values that you need to figure out how to combine, a variable like currentPlayerId is now simply an Int, and isConnected is simply a Bool.

Ultimately, though, if you often find yourself short-circuiting Bool values to Nothing, you may want to consider using guard, when, and unless with the Maybe monad. In this case, though, this would require you to use a monad transformer so that you can either stack your Reader inside MaybeT, or stack Maybe inside ReaderT.