XMonad.Util.PureX

xmonad-contrib-0.15: Third party extensions for xmonad

Copyright | L. S. Leary 2018
License | BSD3-style (see LICENSE)
Maintainer | L. S. Leary
Stability | unstable
Portability | not portable
Safe Haskell | None
Language | Haskell98

XMonad.Util.PureX

Contents

- Usage
- Utility
  - Generalised when* functions
  - Infix operators
  - `WindowSet` operations

Description

Unlike the opaque `IO` actions that `X` actions can wrap, regular reads from the `XConf` and modifications to the `XState` are fundamentally pure—contrary to the current treatment of such actions in most xmonad code. Pure modifications to the `WindowSet` can be readily composed, but due to the need for those modifications to be properly handled by `windows`, other pure changes to the `XState` cannot be interleaved with those changes to the `WindowSet` without superfluous refreshes, hence breaking composability.

This module aims to rectify that situation by drawing attention to it and providing `PureX`: a pure type with the same monadic interface to state as `X`. The `XLike` typeclass enables writing actions generic over the two monads; if pure, existing `X` actions can be generalised with only a change to the type signature. Various other utilities are provided, in particular the `defile` function which is needed by end-users.

Synopsis

- data PureX a
- class (MonadReader XConf m, MonadState XState m) => XLike m where
  - toX :: m a -> X a
- defile :: PureX Any -> X ()
- windowBracket' :: XLike m => (a -> Bool) -> m a -> X a
- handlingRefresh :: X () -> X ()
- runPureX :: PureX a -> XConf -> XState -> (a, XState)
- toXLike :: XLike m => PureX a -> m a
- when' :: (Monad m, Monoid a) => Bool -> m a -> m a
- whenM' :: (Monad m, Monoid a) => m Bool -> m a -> m a
- whenJust' :: (Monad m, Monoid b) => Maybe a -> (a -> m b) -> m b
- (<?) :: Monad m => m Any -> m a -> m Any
- (&>) :: Applicative f => f Any -> f Any -> f Any
- withWindowSet' :: XLike m => (WindowSet -> m a) -> m a
- withFocii :: (XLike m, Monoid a) => (WorkspaceId -> Window -> m a) -> m a
- modify'' :: (Maybe (Stack a) -> Maybe (Stack a)) -> StackSet i l a s sd -> StackSet i l a s sd
- modifyWindowSet' :: XLike m => (WindowSet -> WindowSet) -> m ()
- getStack :: XLike m => m (Maybe (Stack Window))
- putStack :: XLike m => Maybe (Stack Window) -> m ()
- peek :: XLike m => m (Maybe Window)
- view :: XLike m => WorkspaceId -> m Any
- greedyView :: XLike m => WorkspaceId -> m Any
- invisiView :: XLike m => WorkspaceId -> m Any
- shift :: XLike m => WorkspaceId -> m Any
- curScreen :: XLike m => m (Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail)
- curWorkspace :: XLike m => m WindowSpace
- curTag :: XLike m => m WorkspaceId
- curScreenId :: XLike m => m ScreenId

# Usage

The suggested pattern of usage for this module is to write composable, pure actions as `XLike m => m Any` or `PureX Any` values, where the encapsulated `Any` value encodes whether or not a refresh is needed to properly institute changes. These values can then be combined monoidally (i.e. with `<>` AKA `<+>`) or with operators such as `<*`, `*>`, `<?` and `&>` to build seamless new actions. The end user can run and handle the effects of the pure actions in the `X` monad by applying the `defile` function, which you may want to re-export. Alternatively, if an action does not make stackset changes that need to be handled by `windows`, it can be written with as an `XLike m => m ()` and used directly.

Unfortunately since layouts must handle messages in the `X` monad, this approach does not quite apply to actions involving them. However a relatively direct translation to impure actions is possible: you can write composable, refresh-tracking actions as `X Any` values, making sure to eschew refresh-inducing functions like `windows` and `sendMessage` in favour of `modifyWindowSet` and utilities provided by XMonad.Actions.MessageFeedback. The `windowBracket_` function recently added to XMonad.Operations is the impure analogue of `defile`. Note that `PureX Any` actions can be composed into impure ones after applying `toX`; don't use `defile` for this. E.g.

    windowBracket_ (composableImpureAction <> toX composablePureAction)

Although both `X` and `PureX` have Monoid instances over monoidal values, `(XLike m, Monoid a)` is not enough to infer `Monoid (m a)` (due to the open-world assumption). Hence a `Monoid (m Any)` constraint may need to be used when working with `XLike m => m Any` where no context is forcing `m` to unify with `X` or `PureX`. This can also be avoided by working with `PureX Any` values and generalising them with `toXLike` where necessary.

`PureX` also enables a more monadic style when writing windowset operations; see the implementation of the utilities in this module for examples. For an example of a whole module written in terms of this one, see XMonad.Hooks.RefocusLast.

data PureX a #

The `PureX` newtype over `ReaderT XConf (State XState) a`.

Instances

Monad PureX # |
Instance details Defined in XMonad.Util.PureX Methods (>>=) :: PureX a -> (a -> PureX b) -> PureX b # (>>) :: PureX a -> PureX b -> PureX b # return :: a -> PureX a # fail :: String -> PureX a #
Functor PureX # |
Instance details Defined in XMonad.Util.PureX Methods fmap :: (a -> b) -> PureX a -> PureX b # (<$) :: a -> PureX b -> PureX a #
Applicative PureX # |
Instance details Defined in XMonad.Util.PureX Methods pure :: a -> PureX a # (<*>) :: PureX (a -> b) -> PureX a -> PureX b # liftA2 :: (a -> b -> c) -> PureX a -> PureX b -> PureX c # (*>) :: PureX a -> PureX b -> PureX b # (<*) :: PureX a -> PureX b -> PureX a #
XLike PureX # |
Instance details Defined in XMonad.Util.PureX Methods toX :: PureX a -> X a #
MonadState XState PureX # |
Instance details Defined in XMonad.Util.PureX Methods get :: PureX XState # put :: XState -> PureX () # state :: (XState -> (a, XState)) -> PureX a #
MonadReader XConf PureX # |
Instance details Defined in XMonad.Util.PureX Methods ask :: PureX XConf # local :: (XConf -> XConf) -> PureX a -> PureX a # reader :: (XConf -> a) -> PureX a #
Semigroup a => Semigroup (PureX a) # |
Instance details Defined in XMonad.Util.PureX Methods (<>) :: PureX a -> PureX a -> PureX a # sconcat :: NonEmpty (PureX a) -> PureX a # stimes :: Integral b => b -> PureX a -> PureX a #
Monoid a => Monoid (PureX a) # |
Instance details Defined in XMonad.Util.PureX Methods mempty :: PureX a # mappend :: PureX a -> PureX a -> PureX a # mconcat :: [PureX a] -> PureX a #

class (MonadReader XConf m, MonadState XState m) => XLike m where #

The `XLike` typeclass over monads reading `XConf` values and tracking `XState` state.

Methods

toX :: m a -> X a #

Instances

XLike X # |
Instance details Defined in XMonad.Util.PureX Methods toX :: X a -> X a #
XLike PureX # |
Instance details Defined in XMonad.Util.PureX Methods toX :: PureX a -> X a #

defile :: PureX Any -> X () #

A version of `windowBracket'` specialised to take a `PureX Any` action and handle windowset changes with a refresh when the `Any` holds `True`. Analogous to `windowBracket_`. Don't bake this into your action; it's for the end-user.

windowBracket' :: XLike m => (a -> Bool) -> m a -> X a #

A generalisation of `windowBracket`. Handles refreshing for an action that performs no refresh of its own but can indicate that it needs one through a return value that's tested against the supplied predicate. The action can interleave changes to the `WindowSet` with `IO` or changes to the `XState`.

handlingRefresh :: X () -> X () #

A version of `windowBracket` specialised to take an `X ()` action and perform a refresh handling any changes it makes.

runPureX :: PureX a -> XConf -> XState -> (a, XState) #

Consume a `PureX a`.

toXLike :: XLike m => PureX a -> m a #

Despite appearing less general, `PureX a` is actually isomorphic to `XLike m => m a`.

# Utility

## Generalised when* functions

when' :: (Monad m, Monoid a) => Bool -> m a -> m a #

A `when` that accepts a monoidal return value.

whenM' :: (Monad m, Monoid a) => m Bool -> m a -> m a #

A `whenX`/`whenM` that accepts a monoidal return value.

whenJust' :: (Monad m, Monoid b) => Maybe a -> (a -> m b) -> m b #

A `whenJust` that accepts a monoidal return value.

## Infix operators

(<?) :: Monad m => m Any -> m a -> m Any infixl 4 #

Akin to `<*`. Discarding the wrapped value in the second argument either way, keep its effects iff the first argument returns `Any True`.

(&>) :: Applicative f => f Any -> f Any -> f Any infixl 1 #

Akin to a low precedence `<>`. Combines applicative effects left-to-right and wrapped `Bool`s with `&&` (instead of `||`).

## `WindowSet` operations

withWindowSet' :: XLike m => (WindowSet -> m a) -> m a #

A generalisation of `withWindowSet`.

withFocii :: (XLike m, Monoid a) => (WorkspaceId -> Window -> m a) -> m a #

If there is a current tag and a focused window, perform an operation with them, otherwise return mempty.

modify'' :: (Maybe (Stack a) -> Maybe (Stack a)) -> StackSet i l a s sd -> StackSet i l a s sd #

A variant of `W.modify` and `W.modify'` handling the `Nothing` and `Just` cases uniformly.

modifyWindowSet' :: XLike m => (WindowSet -> WindowSet) -> m () #

A generalisation of `modifyWindowSet`.

getStack :: XLike m => m (Maybe (Stack Window)) #

Get the stack from the current workspace.

putStack :: XLike m => Maybe (Stack Window) -> m () #

Set the stack on the current workspace.

peek :: XLike m => m (Maybe Window) #

Get the focused window if there is one.

view :: XLike m => WorkspaceId -> m Any #

A version of `W.view` that tracks the need to refresh.

greedyView :: XLike m => WorkspaceId -> m Any #

A version of `W.greedyView` that tracks the need to refresh.

invisiView :: XLike m => WorkspaceId -> m Any #

View a workspace if it's not visible. An alternative to `view` and `greedyView` that—rather than changing the current screen or affecting another—opts not to act.

shift :: XLike m => WorkspaceId -> m Any #

A refresh-tracking version of `W.Shift`.

curScreen :: XLike m => m (Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail) #

Get the current screen.

curWorkspace :: XLike m => m WindowSpace #

Get the current workspace.

curTag :: XLike m => m WorkspaceId #

Get the current tag.

curScreenId :: XLike m => m ScreenId #

Get the current `ScreenId`.