Haskell and GUI

I saw an introduction to GUI programming in Haskell by Vladislav Zavialov. In his video, he referred to Unix-like operating systems but not Windows. I wanted to reproduce his example on Windows. As is often the case, it was not straightforward.

A test project

I created a new project gtkTest using stack new gtkTest and changed to its root directory with cd gtkTest.

The Stack-supplied MSYS2 is found in a directory at the output of stack path --programs. On my machine, it is in directory msys2-20210604. I made sure that MSYS2 was up to date by running stack exec -- pacman -Syu repeatedly until it reported everything was up to date and there was nothing to do.

Following the guide in the haskell-gi wiki, I set two environment variables to the absolute path to directories in the Stack-supplied MSYS2: PKG_CONFIG_PATH to mingw64\lib\pkgconfig and XDG_DATA_DIRS to mingw64\share. I knew that Stack would already set PATH in its stack exec environment.

By default, the pkg-config tool looks for .pc files in the directories yielded by stack exec -- pkg-config --variable pc_path pkg-config, which are ../lib/pkgconfig; ../share/pkgconfig. PKG_CONFIG_PATH provides a list of paths to search first, before searching the defaults.

I tried to install MSYS2 pkg-config as a necessary dependency, but discovered that I already had pkgconfig, which is an alternative.

I installed MSYS2 gobject-introspection with stack exec -- pacman - S mingw-w64-x86_64-gobject-introspection.

The GTK project had moved on since I had last investigated it. The latest stable version of GTK was 4.6.5. GTK version 3.24.34 was described as the latest ‘old’ stable version. I first installed GTK3 (version 3.24.34-1) and its dependencies with stack exec -- pacman -S mingw-w64-x86_64-gtksourceview4. mingw-w64-x86_64-gtk3 is a dependency of gtksourceview4.

hello, world

The haskell-gi wiki provides its own example program, namely:

Using Hpack, the package.yaml is:

I used a stack.yaml file based on GHC 9.0.2, namely:

Stack’s resolver provided the latest version of gi-gtk on Hackage in the 3.0 series. There is a separate 4.0 series of gi-gtk versions, that provide bindings to GTK4.

stack build and stack exec gtkTest yield the expected GUI application:

GTK4

So far, so good. The complexity started with GTK4. By analogy with GTK3, I installed mingw-w64-x86_64-gtksourceview5. As well as version 4.6.5-1 of gtk4, this installed mingw-w64-x86_64-crt-git-10.0.0.r32.g89bacd2be-1 as one of its many dependencies.

Now, stack build failed during linking, with:

The undefined references are to symbols mingw_app_type, mingw_initcharmax, mingw_initltssuo_force, mingw_initltsdyn_force and mingw_initltsdrot_force. lib\crt2.o is a file provided by MSYS2 mingw-w64-x86_64-crt-git.

It transpires that bits of MSYS2 used by versions of GHC before 9.4.1 conflict with the latest mingw-w64-x86_64-crt-git. To avoid the conflict, it was necessary to roll back with stack exec -- pacman -Rcns mingw-w64-x86_64-gtksourceview5.

GHC 9.4.1-alpha3

GHC 9.4.1 is still in alpha. However, I was able to use GTK4 on Windows with Stack and GHC 9.4.1-alpha3.

I caused Stack to download and install GHC 9.4.1-alpha3 with a local my-stack-setup.yaml and the stack --setup-info-yaml=my-stack-setup.yaml option:

I modified the example program for the GTK4 API:

The associated package.yaml was:

GHC 9.4.1 ships with base-4.17.0.0, which is not yet on Hackage. I discovered that vector-algorithms is incompatible with the latest version of vector, so I specified vector < 0.13.

For the stack.yaml resolver, I could use ghc-9.4.1 but this meant that I had to specify all the dependencies not provided with the compiler.

I discovered that I needed to use allow-newer: true. I also discovered that forced stack build to use a version of Win32 before 2.13.1 and that mintty needed to be told that expressly. So, I used stack build --flag mintty:-win32-2-13-1. That yielded (in part):

Oddly, the directory in which gtkTest.exe was installed was not the one added to the PATH in the stack exec environment. To run the executable, I had to give the path to it:

The command yielded the expected GUI application: