| 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
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
- 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.
The PureX newtype over ReaderT XConf (State XState) a.
class (MonadReader XConf m, MonadState XState m) => XLike m where #
The XLike typeclass over monads reading XConf values and tracking
XState state.
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.
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
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 Bools 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.
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.