Writing keyboard shortcuts for XMonad in native Haskell is straightforward. Yet there is an even shorter, “EZ” notation.

In this tutorial we go over the relevant ways to write EZ shortcuts for XMonad:

  1. how to write EZ shortcuts and add them to the keys setting in XMonad’s main datastructure XConfig.
  2. how to check if our shortcuts are valid.

Emacs “EZ” shortcut notation

A native Haskell shortcut looks as follows:

((modMask .|. shiftMask, xK_Return), spawn $ terminal conf) 

This shortcut opens a new terminal when we press Mod + Shift + Enter at the same time.

It’s simply a pair of a key combination and an action:

(<key-combination>, <action>)

Since this is normal Haskell, it can be checked by XMonad and doesn’t compile if we make a syntax error.

However, there is an Emacs-like notation with plain strings, called “EZ”, that is nicer to read and write

("M-S-<Enter>", spawn $ terminal conf)

but cannot be checked (that easily) by XMonad. ➡ how to check

In this notation the upper case "M" represents the modMask key, the upper case "S" the shift key, and normal keys like letters and digits are written in lower case.

Key Note
"M" Standard Mod key.
"C" The Ctrl key.
"S" The Shift key.
"M1"-"M5" The keyboard modifier keys. ➡ Mod1 to Mod5
"<Enter>" The Enter key.

There’s a list of all the EZ key codes ➡ Documentation which includes other special keys like volume up/down, mute, display brightness etc. as well as normal keys.
More: Useful shortcut examples
More: Haskell native shortcut notation
More: What are mod-keys?
More: How to change XMonad’s modkey
See: EZ documentation
See: The best editor: (Doom) Emacs

Add our own shortcuts

To add our own shortcuts we first have to define them and then add them to our XConfig value:

import XMonad.EZConfig (additionalKeysP)

-- myKeys :: XConfig -> List 
myKeys conf = 
 [ ("M-f", spawn "firefox")
 , ("M-a s", spawn "steam")
 , ("M-a k", spawn "keypassxc")

-- myConfig :: XConfig
-- Set everything up except keys
myConfig = def
  { --  all other settings
  , modMask = mod4Mask

-- Now add our keys
myConfig' = myConfig `additionalKeysP` (myKeys myConfig)
Load values from other Haskell packages. ➡ How to import
Custom name for our function that takes an XConfig value as input and returns a list with our custom shortcuts. ➡ Haskell function syntax
The single argument to the myKeys function.
[ .. , .. ]
A list in Haskell. Elements are separated by comma.
A key combination for Mod + f at the same time.
"M-a s"
A key combination for an Emacs-like “key sequence”: First press Mod + a at the same time, release, then press only s.
An function that runs a terminal command, e.g. spawn "firefox" launches a Firefox browser. ➡ documentation
XMonad variable that holds a default XConfig value configured by the XMonad creators. ➡ source code
Custom variable for our configured XConfig value but without our shortcuts.
def { modMask = mod4Mask }
Create a copy of def containing our modifications. We replace the old modMask value with mod4Mask, which represents the Win key. ➡ Override notation
A slight variant of myConfig that also contains our custom shortcuts. The single quote ' at the end (pronounced “prime” in Math and Haskell) makes it a completely different name to XMonad, but we use it to express that it’s just a small variation of myConfig (without the single quote). In Haskell it is common to add ' to a name of a value that is just a small variation of another value, because inventing new good names is hard.
A helper function (in infix notation) to converts our EZ shortcuts into native ones and adds them to the built-in shortcuts. It takes two arguments, an XConfig value and a list of shortcuts, and creates a new XConfig value. ➡ Function infix notation
➡ Documentation

A specialty of this notation is that we can define “key sequences” like "M-a s" and "M-a k". We first press Mod + a simulateneously, release, then press s. This allows us to group related XMonad actions under a common key combination prefix like "M-a" with a mnemonic “a for apps”.

More: In-depth XMonad (Haskell native) shortcuts
More: Useful shortcut examples
More: How to change XMonad’s modkey
See: EZ documentation
See: List of all EZ keys

Check EZ keymaps for validity

Key combinations like "M-f" are written as plain strings, so the Haskell compiler can’t check if our string represents a real key combination. As a workaround there is a helper function checkKeymap that we can add to our startup hooks at the top of all the others:

-- myKeys = ...

myStartupHook = do
  return ()
  checkKeymap myConfig' (myKeys myConfig')

myConfig = def
  { startupHook = myStartupHook
  -- other settings

-- myConfig' = myConfig `additionalKeysP` (myKeys myConfig)

This hook shows an error window if we have a typo or accidently use a key combination twice. Startup hooks are actions that are performed at every (re)start of XMonad. These can be actions like starting background apps or in this case checking your key combinations for validity and warning us if not. You should add the return () line before checkKeymap to avoid a possible infinite loop, because myStartupHook depends on myConfig', which in turn depends on myStartupHook.
More: Startup hooks
See: checkKeymap documentation

Tags: xmonad shortcut haskell key binding emacs ez keymap beginner

Malte Neuss

Java Software Engineer by day, Haskell enthusiast by night.

Other Posts In Series

Just Enough Haskell For XMonad

How To Configure Workspace Shortcuts

The keyboard shortcuts in XMonad to manage screens, workspaces and windows often use hardcore Haskell syntax that may be difficult to understand. But it’s just unfamilar.

Read More

Just Enough Haskell For XMonad

Useful XMonad shortcuts

Knowing and customizing shortcuts is the essence of being productive with XMonad. To get some inspiration it’s helpful to look at how built-in and other shortcuts are defined.

Read More

Just Enough Haskell For XMonad

XMonad Layouts

With XMonad you can not only control which apps are shown on which screen or workspace but also how app windows are arranged on a single workspace: These window arrangements are called layouts.

Read More