Writing prettier Haskell with Unicode Syntax and Vim

Jun 21, 2023

A short write-up on combining digraphs, a feature built-in to vim, and Haskell's UnicodeSyntax extension, to easily write beautiful Haskell programs with unicode symbols.

1 Haskell’s Unicode Syntax Extension

Haskell (well, GHC Haskell) features an extension called UnicodeSyntax. When enabled, this extension allows the use of certain unicode symbols in place of their corresponding keywords. A great example is the forall keyword being equivalent to the unicode symbol , the two of which can be used interchangebly when UnicodeSyntax is enabled.

Furthermore, with Haskell being a unicode-friendly language, one can define common Haskell functions, operators or type variables using unicode symbols – which doesn’t even require UnicodeSyntax to be enabled. For example, one can define the predicate on lists as an alias for elem as follows:

-- 5 ∈ [1,3,5] == True
(∈) :: ∀ α. Eq α => α -> [α] -> Bool
(∈) = elem

In practice, I use just a handful of unicode symbols both as keywords and as identifiers, but a mostly comprehensive list of the keywords that have unicode alternatives is presented in the GHC user’s guide UnicodeSyntax extension page. Specifically, in most of my programs you can be sure to find the following:

In my opinion, those are low-hanging niceties (with vim) that make the program look better overall, but there are others that I haven’t yet reached for which you may still find good/useful. For example, there’s a library in hackage, containers-unicode-symbols, which exposes multiple unicode variants of functions on containers (Maps,Sets,…) such as ,,,,,,.

Finally, I usually add default-extensions: UnicodeSyntax to my cabal file to make the extension available by default on all modules. However, you can also enable it on a per module basis as usual with {-# LANGUAGE UnicodeSyntax #-} at the top of the module.

2 Digraphs in Vim

To experiment with UnicodeSyntax, or if you’re already convinced that using unicode symbols makes the programs nicer to look at, all that’s left is to be able to input these symbols easily. Vim has a built-in feature called digraphs that makes inputting unicode symbols a joy. The feature is called digraphs because it maps combinations of exactly two letters to a unicode symbol (see also :help digraph).

To input a digraph, in insert mode, press Ctrl+k followed by the two letters which define the digraph. Here are a few useful, built-in, combinations:

Besides the built-in ones, it’s very useful to define your own digraphs. Both for customization/personalization and ergonomics, but also to introduce digraphs which simply do not exist by default.

To create a digraph, use the digraph VimScript keyword with the two characters that input it, and the decimal numeric representation of the Unicode character it is mapped to. In the following .vimrc snippet, I defined the digraph ll with 8888 (the unicode decimal representation of ⊸), which effectively maps Ctrl-k+ll to .

digraph ll 8888 " ⊸

3 Conclusion

Concluding, vim makes it really easy, through digraphs, to input unicode symbols which are understood by Haskell, and even more so with its UnicodeSyntax extension. Combining these features we can easily write more beautiful Haskell programs. I’d argue it’s as fast to write

lid :: ∀ α. α ⊸ α

as it is to write

lid :: forall a. a %1 -> a

while the former is arguably more aesthetically pleasing.