Implementing FizzBuzz in Haskell and Qt6 via the Foreign Function Interface

Posted on

Table of contents

💜 Acknowledgements

Special thanks to MonoidMusician for all xyr help and support while writing this article!

1 About this article

A while ago I started learning about the Foreign Function Interface and I couldn't really find much information about it that was not really outdated or plain wrong.

The information that is correct is also spread out in small pieces over many websites, making it more difficult to understand. It can also be confusing if multiple sources say the opposite, which one is correct?

Here I want to bundle a lot of information about FFI by writing a basic application, just something that prints clickable buttons in a window.

1.1 What you should know

I assume you know what the IO-monad is, how monad transformers work and have a basic understanding of C++ (I will not explain for-loops).

1.2 What will be covered

The following topics will be covered in this article:

  • Using foreign import to call C or C++-functions (without name mangling);
  • Using foreign export to call Haskell-functions in C or C++
  • How to create pointers that get freed automatically to prevent memory issues;
  • How to create arrays in Haskell;
  • How to create structs in Haskell (manually and via GStorable);
  • How to create function pointers in Haskell;
  • Why MonadReader (IORef [a]) m is better for storing pointers than MonadState [a] m;
  • The difference between ccall and capi.

1.3 Used technologies

The technologies used in this article are:

  • GHC 9.4.7
  • Cabal 3.10.3.0
  • Qt 6.7.0
  • GCC 14.1.1 (20240507)

2 About FizzBuzz

FizzBuzz is a challenge where you need to print some numbers, replacing numbers divisible by 3, 5 and 15 by Fizz, Buzz and FizzBuzz respectively.

It is a quick test used by many job interviewers to see if their candidate has the basic knowledge of programming. You need some way to iterate, some way to check for conditions and you need to know a little bit about data types - "Fizz" is a string whereas 1 is a number.

When people write it in Haskell they often use the default console output, but that is boring. In this article we will be building FizzBuzz with a nice, colourful user interface!

2.1 Resulting application

At the end of this article we will have a colourful interface that looks like the image below. We will start with something basic, and keep adding onto it until we get to that result.

100 buttons shown in a grid, 10 rows and 10 columns.
What we are going to create

2.2 Basic implementation

Below you can find a basic implementation of FizzBuzz:

#!/usr/bin/env cabal
{- cabal:
	build-depends: base
	ghc-options: -Wno-tabs
-}

module Main where

main :: IO ()
main = putStrLn fizzbuzz100

fizzbuzz100 :: String
fizzbuzz100 = unwords $ fizzbuzz <$> [1..100]

fizzbuzz :: Int -> String
fizzbuzz n
	| n `mod` 15 == 0 = "FizzBuzz"
	| n `mod`  5 == 0 = "Buzz"
	| n `mod`  3 == 0 = "Fizz"
	| otherwise = show n
Basic Haskell implementation of FizzBuzz

To run it, save it in fizzbuzz.hs and run it:

lira@computer:~/Projects/fizzbuzz$ chmod +x ./fizzbuzz.hs
lira@computer:~/Projects/fizzbuzz$ ./fizzbuzz.hs
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz
Executing the basic Haskell implementation

Quick sidenote: I am only using String here to keep the code short. From now on we will use the proper Data.Text!

3 Showing the result in a pop-up

3.1 Setting up the project with Qt6

Our user interface will be build with Qt6. As a first step, lets show a pop-up that contains the same text as was printed to the console.

The Qt6-API is written in C++, which is also the language we will use to connect to it. This means we will have three files: a Haskell module (Main.hs), some C++-glue (ui.cpp) and the project cabal file. Lets set it up!

Keep in mind that you need cabal 3.8+ because earlier versions were so hardwired to Hackage that this package also named fizzbuzz would cause conflicts...

lira@computer:~/Projects$ mkdir fizzbuzz && cd fizzbuzz
lira@computer:~/Projects/fizzbuzz$ cabal init --simple --minimal --exe
[Log] Using cabal specification: 3.0
[Warning] unknown license type, you must put a copy in LICENSE yourself.
[Log] Creating fresh file CHANGELOG.md...
[Log] Creating fresh directory ./app...
[Log] Creating fresh file app/Main.hs...
[Log] Creating fresh file fizzbuzz.cabal...
[Warning] No synopsis given. You should edit the .cabal file and add one.
[Info] You may want to edit the .cabal file and add a Description field.
lira@computer:~/Projects/fizzbuzz$ mkdir cbits && touch cbits/ui.cpp
lira@computer:~/Projects/fizzbuzz$ ls -lR {**,.}/*.{hs,cpp,cabal} 2>/dev/null
-rw-r--r-- 1 Lira Lira  67 Jan 01 01:01  app/Main.hs
-rw-r--r-- 1 Lira Lira   0 Jan 01 01:01  cbits/ui.cpp
-rw-r--r-- 1 Lira Lira 369 Jan 01 01:01  ./fizzbuzz.cabal
lira@computer:~/Projects/fizzbuzz$ cabal run
Hello, Haskell!
Setting up

In order for Haskell to call code from C++ we need to add some things to the cabal file:

cabal-version: 3.0

name: fizzbuzz
version: 1

executable fizzbuzz
	main-is: Main.hs
	build-depends: base, text
	hs-source-dirs: app
	default-language: Haskell2010

	ghc-options: -Wall -Wno-tabs

	pkgconfig-depends: Qt6Widgets
	cxx-sources: cbits/ui.cpp
	cxx-options: -std=c++23
	extra-libraries: stdc++
Cabal file with configuration for the C++-side of things
pkgconfig-depends

The command pkg-config will be used to find the needed linker flags and cflags so we do not need to write these out ourselves manually. Run pkg-config --cflags Qt6Widgets and pkg-config --libs Qt6Widgets to view the retrieved flags.

lira@computer:~/Projects/fizzbuzz$ pkg-config --cflags Qt6Widgets
-I/usr/include/qt6/QtWidgets -I/usr/include/qt6 -DQT_WIDGETS_LIB -I/usr/lib/qt6/mkspecs/linux-g++ -I/usr/include/qt6/QtGui -DQT_GUI_LIB -I/usr/include/qt6/QtCore -DQT_CORE_LIB
lira@computer:~/Projects/fizzbuzz$ pkg-config --libs Qt6Widgets
-lQt6Widgets -lQt6Gui -lQt6Core
Output of pkg-config
cxx-sources
The .cpp-files we want to include in our build.
cxx-options
Extra options we want to give to the compiler. With -std we can specify the C++-version. We use the latest version, C++23, but a lower version like C++17 also works.
extra-libraries
Here we can specify the standard library, stdc++.

3.2 Name mangling

The functions we will call need to be defined on the C++-side and need to be callable from the Haskell-side. This means that on both sides the name should be the same.

The problem is, C++ supports function overloading, and solves the issue with uniqueness by changing the name behind the scenes based on the parameters. This means there is no way to know what the function will be called in the end after the name mangling when it gets compiled.

If we have two functions void test(int foo) and void test(int foo, int bar), they will have their name changed (mangled) to something like void voidTestint(int foo) and void voidTestintint(int foo, int bar) so they are unique again. What that name is depends on the compiler.

We can solve this by enclosing the functions whose names we don't want to get mangled in an extern "C"-block. This will disable features like function overloading and forces us to only use C-only types in the signature.

The extern "C" block can be used on only one function as well, as shown by test3 in the following example.

extern "C" {
	void test1(int foo){
		//...
	}
	void test2(int foo){
		//...
	}
}

extern "C" void test3(int foo){
	//...
}
An extern "C"-block enclosing two functions test1 and test2 between curly brackets and an extern "C"-block containing the function test3

Another way to prevent name mangling in GCC is with the asm-declaration.

void test(int foo) asm("test");
void test(int foo){
	//...
}
An asm-declaration

3.3 Writing the pop-up code

To create a pop-up we need to start Qt6 by making a QApplication. When the application is started, we can show the pop-up with QMessageBox' exec.

#include<QApplication>
#include<QMessageBox>

extern "C" {
	void showFizzbuzz(const char* message){
		int x = 1;
		QApplication app(x, new char*[1] { (char*)"Fizzbuzz" });
		QMessageBox msgBox;

		msgBox.setText(message);
		msgBox.exec();

		app.quit();
	}
}
Function that shows a pop-up with the given input

After the pop-up is closed, we call app.quit and the application closes.

3.4 Datatypes

Haskell has a nice type system. C does not. When we call functions via FFI we cannot use types like Text or Integer.

The Foreign.*-modules have the types that map to C we need for FFI:

Fully qualified Haskell types that match with C-types[1]
Haskell Haskells C-types C
t :: Char t :: Foreign.C.Types.CChar char t
t :: Int t :: Foreign.C.Types.CInt int t
t :: Text t :: Foreign.C.String.CString (alias of Ptr CChar) char* t
t :: a t :: Foreign.Ptr.Ptr a a* t
t :: IO () t :: IO () void t
t :: IO a (as a value) t :: IO a a t
t :: IO a (as an action) t :: Foreign.Ptr.FunPtr (IO a) a (* t)()
t :: a -> IO b t :: Foreign.Ptr.FunPtr (a -> IO b) b (* t)(a)

3.5 Foreign imports

The syntax for importing foreign functions, like showFizzbuzz, is:

foreign import convention "function_name" haskell_function_name :: TYPE
foreign import syntax

There are two main conventions used in Haskell: ccall and capi. Both are used to call c-functions, or functions exported via a c-interface.

A lot of tutorials will tell you to always use capi, but as we will see later that is not always possible. For now I will only use the convention ccall since that almost always works and is easier to explain. Later we will cover capi and see when one is better than the other depending on the situation.

3.6 Foreign imports with IO

All C-functions have side-effects, and as such have a return type of IO a.

Let's say that we have the function int add5(int foo). We import it as add5 :: CInt -> IO CInt.

--Safe!
foreign import ccall "add5" add5 :: CInt -> IO CInt
Importing add5 safely

We can also import it with a return type a. Doing it this way is essentially the same as what we did before, but with unsafePerformIO called on the result.

--Not safe!
foreign import ccall "add5" add5 :: CInt -> CInt
Importing add5 unsafely

The second option should only be used when you know for certain the function does not have side-effects. In this article we always import with IO.

3.7 Opening the pop-up from Haskell

3.7.1 Importing a c++-function

In order to use void showFizzbuzz(const char* message), we have to import it. void matches with IO (), const char* matches with CString.

foreign import ccall "showFizzbuzz" c_showFizzbuzz :: CString -> IO ()
Importing showFizzbuzz

You might notice the prefix c_. This is done so we can still create a function showFizzbuzz that we will use in our application, and has parameters with non-C-datatypes. We don't want to work with CString in the rest of our application!

3.7.2 withCString

Freeing memory is not something we have to deal with normally in Haskell. With the FFI we do need to think about it. Or better yet, we have to make Haskell think about it.

There are functions like newCString that allow you to create CStrings but they also require you to keep track of the pointers yourself.

This is where withCString :: Text -> (CString -> IO ()) -> IO () comes in handy! You give it a Text, a function that needs the CString and it will run it for you and also free up the memory afterwards, even when exceptions are thrown.

With withCString we can write the showFizzbuzz-function we want to use in the rest of our application:

{-# LANGUAGE OverloadedStrings #-}
module Main where
import Prelude hiding (unwords)
import Data.Text (Text, pack, unwords)
import Data.Text.Foreign (withCString)
import Foreign.C.String (CString)

main :: IO ()
main = showFizzbuzz fizzbuzz100

foreign import ccall "showFizzbuzz" c_showFizzbuzz :: CString -> IO ()
showFizzbuzz :: Text -> IO ()
showFizzbuzz t = withCString t c_showFizzbuzz

fizzbuzz100 :: Text
fizzbuzz100 = unwords $ fizzbuzz <$> [1..100]

fizzbuzz :: Int -> Text
fizzbuzz n
	| n `mod` 15 == 0 = "Fizzbuzz"
	| n `mod`  5 == 0 = "Buzz"
	| n `mod`  3 == 0 = "Fizz"
	| otherwise = pack $ show n
Fizzbuzz popup

3.8 Screenshot

Popup with the only the output of fizzbuzz as output
The pop-up that will be shown

4 Imports used from now on

Keeping track of which imports to add or remove can be a bit annoying.

For the rest of this article, the following build-depends will be needed for us to use the continuation-, state-, reader- and unliftIO-monad; and for deriving Storable automatically:

executable fizzbuzz
--...
	build-depends:
		base, text, mtl, derive-storable, unliftio-core
--...
Final build-depends

We need the following Qt6-imports:

#include<QtWidgets>
Final Qt6-imports

We need the following imports in Haskell

{-# LANGUAGE OverloadedStrings, FlexibleContexts, OverloadedRecordDot, NamedFieldPuns, DeriveGeneric, CApiFFI #-}
module Main where
import Control.Exception (bracket, finally)
import Control.Monad.Cont (cont, runCont)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.IO.Unlift (MonadUnliftIO, askRunInIO)
import Control.Monad.Reader (MonadReader, runReaderT, ask)
import Data.IORef (IORef, newIORef, readIORef, modifyIORef')
import Data.Text (Text, pack)
import Data.Text.Foreign (withCString)
import Foreign.C.Types (CInt(CInt))
import Foreign.C.String (CString)
import Foreign.Marshal.Alloc (malloc, free)
import Foreign.Marshal.Array (withArrayLen)
import Foreign.Ptr (Ptr, FunPtr, freeHaskellFunPtr)
import Foreign.Storable (Storable(poke))
import Foreign.Storable.Generic (GStorable)
import GHC.Generics (Generic)
Final Haskell imports

5 Showing the results as separate labels in a window

5.1 Modifying the pop-up code

Passing an entire string as the result is one option, but a better one might be to pass all the different results and print them individually. In order to do that, we first need to change the signature of showFuzzbuzz:

void showFizzbuzz(int numbersLength, const char** numbers){
Signature of showFizzbuzz in C++

We now accept both a list of char*'s and its length, the latter we need in order to know how much we have to iterate over the numbers.

In showFizzbuzz we now also have to change the code from showing a pop-up to showing a grid with labels:

auto window = new QWidget();
auto box = new QGridLayout(window);

for(auto i = 0; i < numbersLength; i++){
	auto label = new QLabel();
	label->setText(numbers[i]);
	box->addWidget(label, floor(i / 10), i % 10);
}

window->show();

app.exec();
Showing the labels

5.2 Modifying the imports to showFuzzbuzz

Now we need to update the imports to match the signature on C++'s side.

foreign import ccall "showFizzbuzz" c_showFizzbuzz :: CInt -> Ptr CString -> IO ()
showFizzbuzz :: [Text] -> IO ()
showFizzbuzz t = withCStringArray t c_showFizzbuzz
Import in Haskell

There is only one problem: there is no such thing as withCStringArray, we will have to write that ourselves!

5.3 Converting a list to an array with the continuation monad

Sending a string is nice and all, but it would have been nicer if we could send the 100 strings to the function directly, instead of having to unwords them into a single string.

There is one problem: withCString accepts a function that deals with the CString instead of giving it so we can use it ourselves. Converting multiple strings would look like this:

f = withCString a \a' -> do
	withCString b \b' -> do
		withCString c \c' -> do
			c_foo a' b' c'
Converting multiple strings

We cannot convert a list this way, since the length is variable.

This is were the continuation monad comes in!

In order to use the continuation monad, we need a way to convert [Text] to a Cont (IO a) [CString]:

convertListToCont :: [Text] -> Cont (IO ()) [CString]
convertListToCont t = traverse (cont . withCString) t
Getting our Cont

Next we need a way to execute the function that accepts the CStrings from the list. We can do that with withArrayLen, which sadly passes a Int as its first argument instead of a CInt, forcing us to convert it with fromIntegral.

execute ::
	(CInt -> Ptr CString -> IO ()) ->
	[CString] ->
	IO ()
execute f xs = withArrayLen' xs f

withArrayLen' :: Storable a => [a] -> (CInt -> Ptr a -> IO ()) -> IO ()
withArrayLen' xs f = withArrayLen xs $ \l xs' -> f (fromIntegral l) xs'
Executing the function

The last step is glueing those two together with <$> and running the continuation with `runCont` id:

withCStringArray ::
	[Text] ->
	(CInt ->  Ptr CString -> IO ()) ->
	IO ()
withCStringArray t f = ((`withArrayLen'` f) <$> traverse (cont . withCString) t) `runCont` id
withCStringArray

Later on we will also need to send other things to the C-side, so we can easily change the code to work on all types of Storable, not just CString, by creating the generic withCXArray that accepts a function like withCString:

withCXArray :: (Storable b) =>
	(a -> (b -> IO ()) -> IO ()) ->
	[a] ->
	(CInt -> Ptr b -> IO ()) ->
	IO ()
withCXArray withCX t f = ((`withArrayLen'` f) <$> traverse (cont . withCX) t) `runCont` id

withCStringArray ::
	(Text -> (CString -> IO ()) -> IO ()) -> [Text] -> (CInt ->  Ptr CString -> IO ()) -> IO ()
withCStringArray = withCXArray withCString
withCXArray

Now if only Foreign.Marshal.Array had a function like this, that would be so handy! But sadly all functions there assume the list you give is already of type Storable a => [a]...

5.4 Updating main

Now main can be written as follows:

main :: IO ()
main = showFizzbuzz $ fizzbuzz <$> [1..100]
new main

5.5 Screenshot

100 labels shown in a grid, 10 rows and 10 columns, showing all the fizzbuzz values. It starts as "1, 2, Fizz, 4, Buzz,...
The labels shown in a grid

6 Showing the results in colourful labels by using structs for metadata

Until now we have only dealt with basic types, but sometimes we want to pass more complex data via the FFI. We can use structs for this.

6.1 Creating the struct

We want to send the text and color, so those two should be stored together. We will also store the original number:

enum Color: int {
	Red = 0,
	Orange = 1,
	Green = 2,
	Blue = 3,
};

struct FizzBuzz {
	int number;
	char* label;
	Color color;
};
Our struct
data Color
	= Red
	| Orange
	| Green
	| Blue
	deriving Enum
data FizzBuzz = FizzBuzz
	{ number :: Int
	, label :: Text
	, color :: Color
	}
data CFizzBuzz = CFizzBuzz
	{ cNumber :: CInt
	, cLabel :: CString
	, cColor :: CInt
	}
The type in Haskell that matches the struct

We will use FizzBuzz in our application, and CFizzBuzz for the FFI.

In order for a type to be passable via the FFI via Haskell, it needs to derive Storable. This means that CFizzBuzz needs to derive Storable

There are libraries that can help you derive Storable automatically, but to see how it works, let's write one by hand.

6.2 Writing a Storable-instance by hand

6.2.1 The anatomy of Storable

The definition of Storable boils down to the following code:

class Storable a where
{-# MINIMAL sizeOf, alignment,
	(peek | peekElemOff | peekByteOff),
	(poke | pokeElemOff | pokeByteOff) #-}

	sizeOf      :: a -> Int
	alignment   :: a -> Int

	peekElemOff :: Ptr a -> Int      -> IO a
	pokeElemOff :: Ptr a -> Int -> a -> IO ()

	peekByteOff :: Ptr b -> Int      -> IO a
	pokeByteOff :: Ptr b -> Int -> a -> IO ()

	peek        :: Ptr a      -> IO a
	poke        :: Ptr a -> a -> IO ()
Structure of Storable

With peek we can get the value from a Ptr, with poke we can put a vaue into a pointer. sizeOf and alignment are needed for specifying where on memory the value can be stored.

We always need to implement sizeOf and alignment. For peeking and poking we can choose which of the three functions we implement: the general case, for an element or for a byte.

6.2.2 sizeOf

The size of a type is the total amount of memory in bytes it will need to be stored. For a char this will be 1, for 32-bit int this will be 4, for a pointer this will be 8 and so on.

6.2.3 alignment

The alignment of a type is at what offset it can be saved.

Instructions are written for specific alignments, and wrong alignments could result in things like multiple reads or values being written over memory page boundaries.

When this is not correctly set it could result in worse performance or even access violations.

6.2.4 The size of a struct

This is where things get tricky, because we cannot just add the size of all the fields together, we have to take the alignments of all these types into consideration as well!

We will have to take the position of the last field, and add its size to it:

offsetNumber, offsetLabel, offsetColor :: Int
alignmentNumber, alignmentLabel, alignmentColor :: Int
sizeOfNumber, sizeOfLabel, sizeOfColor :: Int
alignmentNumber = alignment (undefined :: CInt)
alignmentLabel = alignment (undefined :: CString)
alignmentnColor = alignment (undefined :: CInt)
sizeOfNumber = sizeOf (undefined :: CInt)
sizeOfLabel = sizeOf (undefined :: CString)
sizeOfnColor = sizeOf (undefined :: CInt)
offsetNumber = 0
offsetLabel = offset0 + ((sizeOfNumber `div` alignmentLabel) + 1) * alignmentLabel
offsetColor = offset1 + ((sizeOfLabel `div` alignmentColor) + 1) * alignmentColor

instance Storable CFizzBuzz where
	{- ... -}
	sizeOf _ = offset2 + sizeOfColor
	{- ... -}
sizeOf of CFizzBuzz

We take advantage here of the fact that ⊥, undefined, is a value that exists on all types.

6.2.5 alignment of a struct

With the alignment here we only need to make sure that the alignment of the fields are always correct. As such, the alignment needs to be the highest alignment of the struct.

instance Storable CFizzBuzz where
	{- ... -}
	alignment _ = maximum
		[ alignmentNumber
		, alignmentLabel
		, alignmentColor
		]
	{- ... -}
alignment of CFizzBuzz

6.2.6 poking the struct

When we want to create a struct, we have to poke it into a pointer. When poking we have to poke the fields at the correct place.

We can reuse the offsets we also used for sizeOf.

instance Storable CFizzBuzz where
	{- ... -}
	poke p fb = do
		pokeByteOff p offset0 fb.cNumber
		pokeByteOff p offset1 fb.cLabel
		pokeByteOff p offset2 fb.cColor
	{- ... -}
pokeing to CFizzBuzz

6.2.7 peeking the struct

With peek we can use the applicative style of IO!

instance Storable CFizzBuzz where
	{- ... -}
	peek p = CFizzBuzz
		<$> peekByteOff p offset0
		<*> peekByteOff p offset1
		<*> peekByteOff p offset2
	{- ... -}
peeking from CFizzBuzz

6.2.8 Realizing a more efficient order of fields exist

We just finished the instance, and we just realized something! We can change the order of the fields so it is more efficient!

struct FizzBuzz {
	int number;
	char* label;
	Color color;
};
Our struct, wrong order

With the order as it is now, there will be a gap of 4 bytes between number and label because of label's alignment. This means that the total size of FizzBuzz is 20.

If we move color together with number, color will fill that gap and the total size will be 16.

struct FizzBuzz {
	int number;
	Color color;
	char* label;
};
Our struct, correct order

We could change that, but it would mean changing the implementation at multiple places. Forgetting it in one place, or doing it wrong in one place, could result in our application not working correctly, or even crashing.

When writing structs, it is probably best to use one of the tools that automatically generate these instances.

6.3 Letting GStorable generate our Storable-instance

As we just saw, writing it ourselves is not fun, especially not when having to refactor the code. So lets try the package GStorable!

With GStorable we have to make CFizzBuzz derive Generic and GStorable. When we do that it will automatically have a Storable-instance.

{- ... -}
{-# LANGUAGE DeriveGeneric #-}
{- ... -}

import Foreign.Storable.Generic (GStorable)
import GHC.Generics (Generic)

{- ... -}

data CFizzBuzz = CFizzBuzz
	{ cNumber :: CInt
	, cColor :: CInt
	, cLabel :: CString
	} deriving (Generic)
instance GStorable CFizzBuzz
CFizzBuzz with GStorable

Easy, isn't it?

We also fixed the order in CFizzBuzz! The order in Haskells FizzBuzz is not important and can remain the same.

6.4 Creating a CFizzBuzz

Now that we made CFizzBuzz a Storable we need a way to convert a FizzBuzz into it.

The fields have to be converted, but we also need to make sure that CFizzBuzz gets cleaned up correctly when it is no longer needed.

To create it, we need to poke a CFizzBuzz into a pointer. And we need to keep track of the lifetime of that pointer.

An easy way to do that is with bracket. Its first argument is for creating the pointer, the second for freeing it. After we specified those we get our pointer we can use.

Once we poked CFizzBuzz into the pointer we have to give it to pass it to the second argument of withCFizzBuzz.

{- ... -}
import Foreign.Marshal.Alloc (malloc, free)
{- ... -}

withCFizzBuzz :: FizzBuzz -> (Ptr CFizzBuzz -> IO ()) -> IO ()
withCFizzBuzz fb f = do
	withCString fb.label $ \l -> do
		bracket malloc free $ \ptr -> do
			poke ptr CFizzBuzz
				{ cNumber = fromIntegral fb.number
				, cColor = fromIntegral $ fromEnum fb.color
				, cLabel = l
				}
			f ptr
withCFizzBuzz

6.5 Updating the labels

Now that we have to pass FizzBuzzes, we have to update our functions as well.

Let's start on the C++-side. We have to change the signature, and also how the text should be set.

After that we can add the styling, depending on the color of the item.

void showFizzBuzz(int itemsLength, FizzBuzz** items){
	//...
		label->setText(items[i]->label);
		switch(items[i]->color){
			case Color::Red:
				label->setStyleSheet("QLabel { background-color: red }");
				break;
			case Color::Orange:
				label->setStyleSheet("QLabel { background-color: orange }");
				break;
			case Color::Green:
				label->setStyleSheet("QLabel { background-color: green }");
				break;
			case Color::Blue:
				label->setStyleSheet("QLabel { background-color: blue }");
				break;
		}
		box->addWidget(label, floor(i / 10), i % 10);
	//...
Updating showFizzBuzz

6.6 Updating fizzBuzz

On the Haskell-side we first have to change the foreign import:

foreign import ccall "showFizzbuzz" c_showFizzbuzz :: CInt -> Ptr CFizzBuzz -> IO ()
showFizzbuzz :: [FizzBuzz] -> IO ()
showFizzbuzz t = withCXArray withCFuzzBuzz t c_showFizzbuzz
New foreign import of showFizzBuzz

Next we have to change fizzBuzz to return FizzBuzzes instead of Texts:

fizzBuzz :: Int -> FizzBuzz
fizzBuzz n
	| n `mod` 15 == 0 = FizzBuzz n "Fizzbuzz" Red
	| n `mod`  5 == 0 = FizzBuzz n "Buzz" Orange
	| n `mod`  3 == 0 = FizzBuzz n "Fizz" Green
	| otherwise = FizzBuzz n (pack $ show n) Blue
Changing fizzBuzz to return FizzBuzz

6.7 Screenshot

100 labels shown in a grid, 10 rows and 10 columns, showing all the fizzbuzz values. It starts as a blue 1, a blue 2, a green Fizz, a blue 4, a yellow Buzz,...
The colourful labels shown in a grid

7 Showing the results in colourful buttons that show a pop-up when clicked

7.1 Labels to buttons

While we have a nice looking interface now, it can be confusing what number is behind a Fizz, Buzz of FizzBuzz! We can change the labels to buttons and show the number behind it when clicked.

Changing the labels to buttons is a simple change:

for(auto i = 0; i < numbersLength; i++){
		auto btn = new QPushButton();
		btn->setText(numbers[i]->label);
		//...
		box->addWidget(btn, floor(i / 10), i % 10);
	}
Change QLabel to QPushButton

Now all labels are buttons, but there is still no interaction!

7.2 Hooking a callback to a button

In Qt6 you can hook a callback to a signal. The signal for clicking a QPushButton is QAbstractButton::released().

The type for the callback we want to hook onto it is void (*ButtonClickCallback)():

typedef void (*ButtonClickCallback)();
typedef of ButtonClickCallback

While all buttons will do the same thing, showing the underlying number, lets have FizzBuzz also include the callback to be called.

struct FizzBuzz {
	int number;
	char* label;
	Color color;
	ButtonClickCallback callback;
};
Extending FizzBuzz

Now we can hook this callback to the button

QObject::connect(btn, &QAbstractButton::released, items[i]->callback);
Hooking callback to the button

7.3 Creating a callback

As we did in C++, we also create a type for the callback in Haskell and add it to FizzBuzz.

type ButtonCallback = IO ()

data FizzBuzz = FizzBuzz
	{ number :: Int
	, label :: Text
	, color :: Color
	, callback :: ButtonCallback
	}
type of ButtonCallback

7.4 Putting the Fun in FunPtr

We cannot add a field to CFizzBuzz like this. For CFizzBuzz we need a function pointer, a FunPtr.

data CFizzBuzz = CFizzBuzz
	{ cNumber :: CInt
	, cLabel :: CString
	, cColor :: CInt
	, cCallback :: FunPtr ButtonCallback
	} deriving (Generic)
Adding cCallback to CFizzBuzz

Creating a FunPtr can be done with the function wrapper which we can import via the function interface. The function is some kind of compiler magic, it is not a real function.[2]

foreign import ccall "wrapper" mkButtonCallback :: ButtonCallback -> IO (FunPtr ButtonCallback)
Creating a function to put ButtonCallback in a FunPtr

With this function we can now extend withCFizzBuzz to also add the callback.

withCFizzBuzz fb f = do
	cCallback <- mkButtonCallback fb.callback
	withCString fb.label $ \l -> do
		bracket malloc free $ \ptr -> do
			poke ptr CFizzBuzz
				{ cNumber = fromIntegral fb.number
				, cLabel = l
				, cColor = fromIntegral $ fromEnum fb.color
				, cCallback
				}
			f ptr
Setting cCallback in withCFizzBuzz

7.5 Cleaning up the FunPtrs

Like other pointers, FunPtrs also have to be freed. We can free the function pointers with freeHaskellFunPtr.

We have to clear the pointers when we no longer need the buttons, which is when we quit the application. We cannot do it immediately or we get undefined behaviour.

This means we need to store all the FunPtrs and free them at the end.

But how do we store it?

7.5.1 The naïve way: MonadState [FunPtr] m

Since we need to keep track of something, MonadState seems like a logical choice.

main = do
	let
		funPtrs = []
		funPtrsAfterRun = execStateT (showFizzbuzz $ fizzbuzz <$> [1..100]) funPtrs
	freeHaskellFunPtr `mapM_` funPtrsAfterRun
Updating main to have state

This will work. At least, it will look like it does. And it does free the function pointers! But not always. Not when things go wrong, like when an exception gets thrown somewhere.

It is not possible to catch here and clean up.

We need something that knows about the outside world, and can store values: IORef.

7.5.2 A better MonadState [FunPtr] m: MonadReader (IORef [FunPtr]) m

This means that we don't need MonadState, but we need an IORef that we can access, via a MonadReader

main = do
	funPtrsRef <- newIORef []
	runReaderT (showFizzbuzz $ fizzbuzz <$> [1..100]) funPtrsRef `finally` do
		funPtrs <- readIORef funPtrsRef
		freeHaskellFunPtr `mapM_` funPtrs
Dealing with exceptions

We have to change withFizzBuzz to add the newly created FunPtr to the IORef. This also means we have to change the type to that of a monad reader.

type FunPtrs = IORef [FunPtr (IO ())]

withCFizzBuzz :: (MonadReader FunPtrs m, MonadIO m) 
	=> FizzBuzz -> (Ptr CFizzBuzz -> IO ()) -> m ()
withCFizzBuzz fb f = do
	funPtrs <- ask
	liftIO $ do
		cCallback <- mkButtonCallback fb.callback
		withCString fb.label $ \l -> do
			bracket malloc free $ \ptr -> do
				poke ptr CFizzBuzz
					{ cNumber = fromIntegral fb.number
					, cLabel = l
					, cColor = fromIntegral $ fromEnum fb.color
					, cCallback
					}
				f ptr
		modifyIORef' funPtrs (cCallback:)
Adding FunPtrs to the list

Next we modify showFizzBuzz to pass the IORef we created in main to withCFizzBuzz.

showFizzbuzz :: (MonadIO m, MonadReader FunPtrs m) => [FizzBuzz] -> m ()
showFizzbuzz t = withCXArray withCFizzBuzz t c_showFizzbuzz
Changing showFizzBuzz to also use the reader monad

Uh-oh! This does not work because withCXArray expects a IO, not a (MonadIO m) => m. There is liftIO :: (MonadIO m) => m () -> IO () but we need to opposite, we need to unlift!

7.5.3 Accessing the monad stack within IO with MonadUnliftIO

With MonadUnliftIO we can get the monad stack into IO. askRunInIO gives us the helpler-function run :: m a -> IO a.

With run we can wrap withCFizzBuzz and execute it within withCXArray.

showFizzbuzz :: (MonadIO m, MonadReader FunPtrs m, MonadUnliftIO m) => [FizzBuzz] -> m ()
showFizzbuzz t = do
	run <- askRunInIO
	liftIO $ withCXArray (fb action -> run $ withCFizzBuzz fb action) t c_showFizzbuzz
Making showFizzBuzz a MonadUnliftIO

Another option would have been to take the state out of the monad reader, and run a new reader within withCFizzBuzz. But that would make the code less readable.

7.6 Screenshot

100 buttons shown in a grid, 10 rows and 10 columns.
Our colourful, interactive FizzBuzz

8 Using foreign export

Just like we can import things, we can also export things! To stay in the spirit of little applications like FizzBuzz, lets create a function that prints Hello world and run it on the C++-side!

foreign export ccall printHelloWorld :: IO ()
printHelloWorld :: IO ()
printHelloWorld = putStrLn "Hello world"
Exporting printHelloWorld

With this foreign export Haskell will generate a .h- and .c-file for us, which we can import to get printHelloWorld! The name is the name of the module, followed by _stub.h.

#include "Main_stub.h"

//...

void showFizzbuzz(int itemsLength, FizzBuzz** items){
		int x = 1;
		QApplication app(x, new char*[1] { (char*)"Fizzbuzz" });

		printHelloWorld();

		//...
}
Importing and calling printHelloWorld
lira@computer:~/Projects/fizzbuzz$ cabal run
Hello world
Running with cabal and viewing the logs

9 On CApiFFI

9.1 When to use the CApiFFI-extension

When you want to stick to the language standard, you should always use ccall since capi is a GHC-extension. This means that ccall will also work for other compilers.

When you need wrapper or dynamic to create function pointers or to convert function pointers to proper functions, you also need to use ccall since this is not supported in capi.

When you have a function with a variable number of arguments, you should always use capi since ccall doesn't support it.

That leaves us with the common situation: using GHC Haskell and external code via the C-ABI or C-API. When using the C-ABI, we can keep using ccall, when using the C-API we can use capi.

9.2 Changing the ccalls to capi

For foreign export the only thing we need to do is change ccall to capi

foreign export capi printHelloWorld :: IO ()
printHelloWorld :: IO ()
Changing printHelloWorld to capi

For foreign import we cannot just change ccall to capi, we also need to specify a header file.

Copying the signature of the two functions showMessage and showFizzbuzz also means we have to move Color and FizzBuzz to the header. Keep in mind that we also need to typedef them, otherwise capi will not be able to find the types as it expects struct FizzBuzz instead of FizzBuzz!

#pragma once
#ifdef __cplusplus
extern "C" {
#endif

typedef void (*ButtonClickCallback)();

typedef enum Color: int {
	Red = 0,
	Orange = 1,
	Green = 2,
	Blue = 3,
} Color;

typedef struct FizzBuzz {
	int number;
	char* label;
	Color color;
	ButtonClickCallback callback;
} FizzBuzz;

void showMessage(const char* message);
void showFizzbuzz(int itemsLength, FizzBuzz** items);

#ifdef __cplusplus
}
#endif
The headerfile

As you can see we start the file with #pragma once. This is because the generated code for capi includes this header many times, for each foreign import. You can view this if you run cabal run --ghc-options=-keep-tmp-files and go to the /tmp-folder that gets mentioned in the output — normally something like /tmp/ghcxxxx_0/ghc_2.c.

We also use #ifdef _cplusplus-pragmas around extern "C", because sometimes this header gets read for C (ghc_2.c) and sometimes for C++ (cbits/ui.cpp) and it needs to work for both. Of course, since there is a #pragma once either C or C++ will win so leaving it out will only work sometimes.

With our new header-file fizzbuzz.cabal should now include the following:

extra-source-files: cbits/*.cpp, cbits/*.h

executable fizzbuzz
	--...
	pkgconfig-depends: Qt6Widgets
	cxx-sources: cbits/ui.cpp
	cxx-options: -std=c++17
	includes: ui.h
	include-dirs: ./cbits
	extra-libraries: stdc++
Cabal file with header

Now that we did that... it still doesn't work! capi seems to generate code that maps Ptr CFizzBuzz to void* instead of FizzBuzz*.

error: passing argument 2 of ‘showFizzbuzz’ from incompatible pointer type [-Wincompatible-pointer-types]
   |
31 | void ghczuwrapperZC2ZCmainZCMainZCshowFizzzzbuzzzz(HsInt32 a1, void** a2) {showFizzbuzz(a1, a2);}
   |                                                                                             ^

In file included from /tmp/ghc19182_0/ghc_2.c:28:0: error:

cbits/ui.h:21:62: error:
     note: expected ‘struct FizzBuzz **’ but argument is of type ‘void **’
   |
21 |         void showFizzbuzz(int itemsLength, struct FizzBuzz** items);
   |                                                              ^
Error without CType

This is because we haven't told GHC what CFizzBuzz maps to! For this we need the CType-pragma.

data {-# CTYPE "ui.h" "FizzBuzz" #-} CFizzBuzz = CFizzBuzz
	{ cNumber :: CInt
	, cLabel :: CString
	, cColor :: CInt
	, cCallback :: FunPtr ButtonCallback
	} deriving (Generic)
Adding the CType pragma to CFizzBuzz

Now it works!

As you can see, capi is a lot of extra work but it does give you extra safety! It is best to always use it external libraries outside of your control. For your own code you can choose what fits you better, ccall or capi

10 Final code

{-# LANGUAGE OverloadedStrings, FlexibleContexts, OverloadedRecordDot, NamedFieldPuns, DeriveGeneric, CApiFFI #-}

module Main where

import Control.Exception (bracket, finally)
import Control.Monad.Cont (cont, runCont)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.IO.Unlift (MonadUnliftIO, askRunInIO)
import Control.Monad.Reader (MonadReader, runReaderT, ask)
import Data.IORef (IORef, newIORef, readIORef, modifyIORef')
import Data.Text (Text, pack)
import Data.Text.Foreign (withCString)
import Foreign.C.Types (CInt(CInt))
import Foreign.C.String (CString)
import Foreign.Marshal.Alloc (malloc, free)
import Foreign.Marshal.Array (withArrayLen)
import Foreign.Ptr (Ptr, FunPtr, freeHaskellFunPtr)
import Foreign.Storable (Storable(poke))
import Foreign.Storable.Generic (GStorable)
import GHC.Generics (Generic)

main :: IO ()
main = do
	funPtrsRef <- newIORef []
	runReaderT (showFizzbuzz $ fizzbuzz <$> [1..100]) funPtrsRef `finally` do
		funPtrs <- readIORef funPtrsRef
		freeHaskellFunPtr `mapM_` funPtrs

data Color
	= Red
	| Orange
	| Green
	| Blue
	deriving Enum
type ButtonCallback = IO ()
data FizzBuzz = FizzBuzz
	{ number :: Int
	, label :: Text
	, color :: Color
	, callback :: ButtonCallback
	}

fizzbuzz :: Int -> FizzBuzz
fizzbuzz n
	| n `mod` 15 == 0 = FizzBuzz n "Fizzbuzz" Red click
	| n `mod`  5 == 0 = FizzBuzz n "Buzz" Orange click
	| n `mod`  3 == 0 = FizzBuzz n "Fizz" Green click
	| otherwise = FizzBuzz n (pack $ show n) Blue click
	where
		click :: IO ()
		click = showMessage (pack $ show n)

foreign export capi printHelloWorld :: IO ()
printHelloWorld :: IO ()
printHelloWorld = putStrLn "Hello world"

type FunPtrs = IORef [FunPtr (IO ())]

data {-# CTYPE "ui.h" "FizzBuzz" #-} CFizzBuzz = CFizzBuzz
	{ cNumber :: CInt
	, cLabel :: CString
	, cColor :: CInt
	, cCallback :: FunPtr ButtonCallback
	} deriving (Generic)
instance GStorable CFizzBuzz

foreign import capi "ui.h showFizzbuzz" c_showFizzbuzz :: CInt -> Ptr (Ptr CFizzBuzz) -> IO ()
showFizzbuzz :: (MonadIO m, MonadReader FunPtrs m, MonadUnliftIO m) => [FizzBuzz] -> m ()
showFizzbuzz t = do
	run <- askRunInIO
	liftIO $ withCXArray (\fb action -> run $ withCFizzBuzz fb action) t c_showFizzbuzz

foreign import capi "ui.h showMessage" c_showMessage :: CString -> IO ()
showMessage :: Text -> IO ()
showMessage t = withCString t c_showMessage
foreign import ccall "wrapper" mkButtonCallback :: ButtonCallback -> IO (FunPtr ButtonCallback)

withCXArray :: (Storable b)
	=> (a -> (b -> IO ()) -> IO ()) -> [a] -> (CInt -> Ptr b -> IO ()) -> IO ()
withCXArray withCX t f = ((`withArrayLen'` f) <$> traverse (cont . withCX) t) `runCont` id

withArrayLen' :: Storable a => [a] -> (CInt -> Ptr a -> IO ()) -> IO ()
withArrayLen' xs f = withArrayLen xs $ \l xs' -> f (fromIntegral l) xs'

withCFizzBuzz :: (MonadReader FunPtrs m, MonadIO m) => FizzBuzz -> (Ptr CFizzBuzz -> IO ()) -> m ()
withCFizzBuzz fb f = do
	funPtrs <- ask
	liftIO $ do
		cCallback <- mkButtonCallback fb.callback
		withCString fb.label $ \l -> do
			bracket malloc free $ \ptr -> do
				poke ptr CFizzBuzz
					{ cNumber = fromIntegral fb.number
					, cLabel = l
					, cColor = fromIntegral $ fromEnum fb.color
					, cCallback
					}
				f ptr
		modifyIORef' funPtrs (cCallback:)
Code of app/Main.hs
#pragma once
#ifdef __cplusplus
extern "C" {
#endif

typedef void (*ButtonClickCallback)();

typedef enum Color: int {
	Red = 0,
	Orange = 1,
	Green = 2,
	Blue = 3,
} Color;

typedef struct FizzBuzz {
	int number;
	char* label;
	Color color;
	ButtonClickCallback callback;
} FizzBuzz;

void showMessage(const char* message);
void showFizzbuzz(int itemsLength, FizzBuzz** items);

#ifdef __cplusplus
}
#endif
Code of cbits/ui.h
#include<QApplication>
#include<QGridLayout>
#include<QPushButton>
#include<QMessageBox>

#include "ui.h"
#include "Main_stub.h"

static const char* getStyleFromColor(Color color){
	switch(color){
		case Color::Red:
			return "QPushButton { background-color: red }";
		case Color::Orange:
			return "QPushButton { background-color: orange }";
		case Color::Green:
			return "QPushButton { background-color: green }";
		case Color::Blue:
			return "QPushButton { background-color: blue }";
	}

	return "";
}

extern "C" {

	void showMessage(const char* message){
		QMessageBox msgBox;
		msgBox.setText(message);
		msgBox.exec();
	}

	void showFizzbuzz(int itemsLength, FizzBuzz** items){
		int x = 1;
		QApplication app(x, new char*[1] { (char*)"Fizzbuzz" });

		printHelloWorld();

		auto window = new QWidget();
		auto box = new QGridLayout(window);

		for(auto i = 0; i < itemsLength; i++){
			auto btn = new QPushButton();
			btn->setText(items[i]->label);
			btn->setStyleSheet(getStyleFromColor(items[i]->color));
			QObject::connect(btn, &QAbstractButton::released, items[i]->callback);
			box->addWidget(btn, floor(i / 10), i % 10);
		}

		window->show();

		app.exec();
	}

}
Code of cbits/ui.cpp
cabal-version: 2.4

name: fizzbuzz
version: 1

executable fizzbuzz
	main-is: Main.hs
	build-depends:
		base, text, mtl, derive-storable, unliftio-core
	hs-source-dirs: app
	default-language: Haskell2010

	ghc-options: -Wall -Wno-tabs

	pkgconfig-depends: Qt6Widgets
	cxx-sources: cbits/ui.cpp
	cxx-options: -std=c++17
	includes: ui.h
	include-dirs: ./cbits
	extra-libraries: stdc++
Code of fizzbuzz.cabal

11 Sources

12 Footnotes

  • 1: I am not going to fully qualify IO and () [go back]
  • 2: There is also the magic function dynamic which does the opposite [go back]