Mobile Haskell (iOS)

Versions used:

  • Xcode Version 9.2 (9C40b)
  • Cabal HEAD (commit 94a7374)
  • Stack Version 1.6.3
  • LLVM Version 5.0.1

A lot of progress has been going on to make Haskell work on mobile natively, instead of e.g. generating JavaScript via GHCJS and using that. Unfortunately, not much documentation exists yet on how to build a project using these tools all together.

This post will be an attempt to piece together the tools and various attempts into a coherent step-by-step guide. We will start by setting up the tools needed, and then build an iOS app that runs in both the simulator and on the device itself (i.e. a x86 build and an arm build).

For the impatient and brave, simply,

  • clone down the MobileHaskellFun repository,
  • run ./ to set up the tools,
  • cd into Offie/hs-src/
  • build the package index ./call x86_64-apple-ios-cabal new-update --allow-newer,
  • run ./call make iOS to compile the program for iOS,
  • and finally launch Xcode and start the simulator.

Setting up the Tools

A bunch of tools are needed, so we will set these up first. You might have some of these, but I will go through them anyways, for good measure. The steps will assume that we are on macOS for some parts, but it should not be part to adapt these to your system (all steps using brew).

If you don’t have stack installed already, set it up with,

$ curl -sSL | sh

We will collect all our tools and GHC versions in a folder in $HOME—for convenience—so first we are a going to create that directory,

$ mkdir -p ~/.mobile-haskell

Next step is cloning down cabal and building cabal-install. This is necessary until new-update lands.

$ cd ~/.mobile-haskell
$ git clone [email protected]:haskell/cabal.git
$ cd cabal-install && stack exec --no-ghc-package-path -- ./

If you have cabal-install and a system GHC already, then you can try and install it via cabal new-build cabal-install instead, which is less brittle. I wanted to remove the need to setup these though, so I went with the ./ approach.

NOTE: If you are having trouble with e.g. errors on packages being shadowed, try the good ol’ cabal-hell fix, and nuke ~/.ghc and ~/.cabal/.

Install LLVM version 5,

$ brew install llvm

This should set up LLVM in /usr/local/opt/[email protected]/bin (or just /usr/local/opt/llvm/bin), remember this path for later.

We’ll now set up the tools from, namely the toolchain-wrapper and the different GHC versions we will use.

Let’s start off with getting our GHCs, by downloading ghc- and ghc-, for the simulator and device respectively. You can download the by cliking their links on the website, or curl them down with (the links are probably outdated soon, so replace the links with the ones on the site),

$ cd ~/.mobile-haskell
$ curl -o ghc-aarch64-apple-ios.tar.xz
$ curl -o ghc-x86_64-apple-ios.tar.xz

Now, let’s unpack these into their own folders (assuming you’re still in ~/.mobile-haskell),

$ mkdir -p ghc-aarch64-apple-ios && xz -d ghc-aarch64-apple-ios.tar.xz && tar -xf ghc-aarch64-apple-ios.tar -C ghc-aarch64-apple-ios
$ mkdir -p ghc-x86_64-apple-ios && xz -d ghc-x86_64-apple-ios.tar.xz && tar -xf ghc-x86_64-apple-ios.tar -C ghc-x86_64-apple-ios

Next up is the toolchain-wrapper, which provides wrappers around cabal and other tools we need,

$ cd ~/.mobile-haskell
$ git clone [email protected]:zw3rk/toolchain-wrapper.git
$ cd toolchain-wrapper && ./bootstrap

And that’s it! We have now set up all the tools we need for later. If you want all the steps as a single script, check out the setup script in the MobileHaskellFun repo.

Setting up the Xcode Project

Setting up Xcode is a bit of a visual process, so I’ll augment these steps with pictures, to hopefully make it clear what needs to be done.

First, let’s set up our Xcode project, by creating a new project.

Choose Single View Application,

And set the name and location of your project,

1.2. Create Project - Name 1.3. Create Project - Set Location

Now, let’s add a folder to keep our Haskell code in and call it hs-src, by right-clicking our project and adding a New Group,

2. Add Source Folder for Haskell Code

Interlude: Set up the Haskell Code

Before we proceed, let’s set up the Haskell code. Navigate to the hs-src directory, and add the following files (don’t worry, we’ll go through their contents),

$ mkdir -p src
$ touch MobileFun.cabal cabal.project Makefile call LICENSE src/Lib.hs


We use the features of cabal.project to set our package repository to use the overlay.

packages: .

repository hackage.mobilehaskell
  secure: True
  root-keys: 8184c1f23ce05ab836e5ebac3c3a56eecb486df503cc28110e699e24792582da
  key-threshold: 3


Just a simple cabal package setup.

name:                MobileFun
license:             BSD3
license-file:        LICENSE
author:              Your Name
copyright:           Your Name
category:            Miscellaneous
build-type:          Simple
cabal-version:       >=1.10

  hs-source-dirs:      src
  exposed-modules:     Lib
  build-depends:       base >= 4.7 && < 5
                     , freer-simple
  default-language:    Haskell2010


The Makefile simplifies a lot of the compilation process and passes the flags we need to use.



.PHONY: cabal-build
	$(CABAL) new-configure --disable-shared --enable-static --allow-newer --ghc-option=-fllvmng
	$(CABAL) new-build --allow-newer --ghc-option=-fllvmng

	CABAL=x86_64-apple-ios-cabal make cabal-build
	CABAL=aarch64-apple-ios-cabal make cabal-build
	mkdir -p $(@D)
	find . -path "*-ios*" -name "${ARCHIVE}*ghc*.a" -exec lipo -create -output [email protected] {} +

	CABAL=cabal make cabal-build
	mkdir -p $(@D)
	find . -path "*-osx*" -name "${ARCHIVE}*ghc*.a" -exec lipo -create -output [email protected] {} +

	CABAL=armv7-linux-androideabi-cabal make cabal-build
	mkdir -p $(@D)
	find . -path "*arm-*-android*" -name "${ARCHIVE}*ghc*.a" -exec cp {} [email protected] \;

	CABAL=aarch64-linux-android-cabal make cabal-build
	mkdir -p $(@D)
	find . -path "*aarch64-*-android*" -name "${ARCHIVE}*ghc*.a" -exec cp {} [email protected] \;

.PHONY: android
.PHONY: all
.PHONY: clean
iOS: binaries/iOS/${ARCHIVE}.a
macOS: binaries/macOS/${ARCHIVE}.a
android: binaries/android/armv7a/${ARCHIVE}.a binaries/android/arm64-v8a/${ARCHIVE}.a
all: iOS macOS android
	rm -R binaries


Our Haskell code for now, is simply some C FFI that sets up a small toy function.

module Lib where

import Foreign.C (CString, newCString)

-- | export haskell function @[email protected] as @[email protected]
foreign export ccall "hello" chello :: IO CString

-- | Tiny wrapper to return a CString
chello = newCString hello

-- | Pristine haskell function.
hello = "Hello from Haskell"


We use the call script to set up the various path variables that point to our tools, so we don’t need these polluting our global command space. If you’ve followed the setup so far, the paths should match out-of-the-box.

#!/usr/bin/env bash
# Path to LLVM (this is the default when installing via `brew`)
export PATH=/usr/local/opt/[email protected]/bin:$PATH
# Path to Cross-target GHCs
export PATH=$HOME/.mobile-haskell/ghc-x86_64-apple-ios/bin:$PATH
export PATH=$HOME/.mobile-haskell/ghc-aarch64-apple-ios/bin:$PATH
export PATH=$HOME/.mobile-haskell/ghc-x86_64-apple-darwin/bin:$PATH
# Path to tools.
export PATH=$HOME/.mobile-haskell/toolchain-wrapper:$PATH
export PATH=$HOME/.mobile-haskell/head.hackage/scripts:$PATH
# Path to Cabal HEAD binary.
export PATH=$HOME/.cabal/bin:$PATH

# Pass everything as the command to call.
[email protected]

Compiling Our Haskell Code

First off, we need to build our package index, so run (inside hs-src),

$ ./call x86_64-apple-ios-cabal new-update --allow-newer
Downloading the latest package lists from:
- hackage.mobilehaskell

Now we can build our project by running make on our target. For now, we have only set up iOS, so this is what we will build.

$ ./call make iOS
CABAL=x86_64-apple-ios-cabal make cabal-build
x86_64-apple-ios-cabal new-configure --disable-shared --enable-static --allow-newer --ghc-option=-fllvmng
'cabal.project.local' file already exists. Now overwriting it.
Resolving dependencies...
Build profile: -w ghc- -O1
In order, the following would be built (use -v for more details):
 - natural-transformation-0.4 (lib) (requires download & build)
 - transformers-compat- (lib) (requires download & build)
 - transformers-base-0.4.4 (requires download & build)
find . -path "*-ios*" -name "libHSMobileFun*ghc*.a" -exec lipo -create -output binaries/iOS/libHSMobileFun.a {} +

We should now have our library file at hs-src/binaries/iOS/libHSMobileFun.a. If you change your project, to will probably need to run ./call make clean before running ./call make iOS again.

Back to Xcode

Now we need to tie together the Haskell code with Xcode. Drag-and-drop the newly created files into the hs-src group in Xcode (if it hasn’t found it by itself).

Since we are using Swift, we need a bridging header to bring our C prototypes into Swift. We’ll do this by adding an Objective-C file to the project, tmp.m, which will make Xcode ask if we want to create a bridging header, Offie-Bridging-Header.h, for which we will answer yes.

4. Create Objective-C File

4.1. Create Objective-C File - Choose Filetype 4.2. Create Objective-C File - Set Name

4.3. Create Objective-C File - Set Location 4.4. Create Objective-C File - Create Bridging Header


In our bridging file, Offie-Bridging-Header.h, we add our prototypes that we need to glue in the Haskell code,

extern void hs_init(int * argc, char ** argv[]);
extern char * hello();


Now let’s go into AppDelegate.swift and call hs_init to initialize the Haskell code,

import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        hs_init(nil, nil)
        return true

    func applicationWillResignActive(_ application: UIApplication) {}
    func applicationDidEnterBackground(_ application: UIApplication) {}
    func applicationWillEnterForeground(_ application: UIApplication) {}
    func applicationDidBecomeActive(_ application: UIApplication) {}
    func applicationWillTerminate(_ application: UIApplication) {}


Next, we will set up a label in a view controller. You can either set this up in the story board and connect it via an IBOutlet.

First go into the Main.storyboard and create a label element somewhere on the screen.

Then enable the Assistant Editor in the top right cornor, and ctrl-click on the label, dragging it over to the ViewController.swift and name helloWorldLabel.

We can now set the text of the label by calling our Haskell function with cString: hello(), making our ViewController.swift look like,

import UIKit

class ViewController: UIViewController {

    @IBOutlet var helloWorldLabel: UILabel!

    override func viewDidLoad() {
        helloWorldLabel.text = String(cString: hello())

    override func didReceiveMemoryWarning() {

Linking in our Haskell Library

The final step we need to do, is linking in our library that we built earlier, hs-src/binaries/iOS/libHSMobileFun.a, so that Xcode can find our C prototype functions.

We do this by going into Build Phases, which is exposed under the Xcode project settings, and click the + to add a new library,

Choose Add Other... to locate the library,

and finally locate the library file in hs-src/binaries/iOS/libHSMobileFun.a,

We also need to set the build to not generate bytecode, because we are using the external GHC library. This is done under Build Settings, locating Enable Bitcode (e.g. via the search) and setting it to No.

Run the Code!

Final step, let’s run our code in the simulator

Congratulations! You’re now calling Haskell code from Swift and running it in an iOS simulator.

NOTE: You might run into a problem like could not create compact unwind for _ffi_call_unix64: does not use RBP or RSP based frame in your Xcode builds. You can fix this by adding libconv to your libraries in Build Phase.


Most of this is gathered from:

If you are interested in following the development of Haskell in the mobile space, I recommend following @zw3rktech and @mobilehaskell.

Finally, let me know if something is not working with the MobileHaskellFun repository. I haven’t dealt that much with setting up Xcode projects for sharing, so I’m a bit unclear on what settings follow the repository around.

Using Electron with Haskell

Versions used:

  • Electron 1.0.1
  • Stackage LTS 5.15
  • servant

If you want to grab the whole code from this post, it can be found at codetalkio/Haskell-Electron-app.

Not much literature exist on using Electron as a GUI tool for Haskell development, so I thought I’d explore the space a little. Being initially a bit clueless on how Electron would launch the Haskell web server, I was watching the Electron meetup talk by Mike Craig from Wagon HG (they use Electron with Haskell) and noticed they actually mention it on the slides:

* Statically compiled Haskell executable
* Shipped in Electron app bundle
* main.js in Electron spawns the subprocess
* Executable creates a localhost HTTP server
* JS talks to Haskell over AJAX

Importantly the bit main.js in Electron spawns the subprocess is the part that was somehow missing in my mental model of how this would be structured (my JavaScript experience mainly lies in webdev and not really with Node.js and server-side/desktop JS).

Riding on this epiphany, I decided to document my exploration, seeing as this is an area that is sorely lacking (GUI programming in Haskell in general). Before we start anything though, let me lay out what the project structure will look like:

    ... electron files
    ... servant files

Setting up Electron

Electron has a nice quick start guide, which helps you get going fairly, well, quick. For our purposes, the following will set up the initial app we will use throughout.

In your shell/terminal
$ cd Haskell-Electron-app
$ git clone haskell-app
$ cd haskell-app
$ npm install && npm start

And that’s it really. You’ve now got a basic Electron app running locally. The npm start command is what launches the app for you. But! Before doing anything more here, let’s take a look at the Haskell side.

Setting up the Haskell webserver

We’ll be using servant for a minimal application, but you could really use anything that will run a web server (such as Yesod, WAI, Snap, Happstack etc, you get the idea :).

Assuming that stack is installed, you can set up a new servant project with

In your shell/terminal
$ cd Haskell-Electron-app
$ stack new backend servant
$ cd backend
$ stack build

which will download the servant project template for you (from the stack templates repo) and build it.

To test that it works run stack exec backend-exe which will start the executable that stack build produced. You now have a web server running at - try and navigate to and check it out! :)

For the lack of a better named I have called the application backend, but it could really be anything you fancy.

Contacting Servant/Haskell from Electron

For now, let us proceed with Electron and servant running separately, and later on explore how we can start the servant server from inside Electron.

Since the servant template project has given us the endpoint from which it serves JSON, let’s set up Electron to call that and display the results.

By default the chromium developer tools are enabled in Electron. I suggest you keep them enabled while debugging, since that makes it a lot easier to see if anything went wrong. If you want to disable it, you just need to comment/remove a line in Haskell-Electron-app/haskell-app/main.js:

function createWindow () {
  // Create the browser window,
  mainWindow = new BrowserWindow({width: 800, height: 600})
  // and load the index.html of the app.
  mainWindow.loadURL('file://' + __dirname + '/index.html')
  // Open the DevTools.
  // mainWindow.webContents.openDevTools() <-- this one here

Short interlude: since I’m a bit lazy let’s go ahead and download jQuery 2.2.3 minified and put that into Haskell-Electron-app/haskell-app/resources/jQuery-2.2.3.min.js so we can include it later on and get the nice AJAX functionality it provides.

Back to work, lets change the index.html page and prepare it for our list of users.

<!DOCTYPE html>
    <meta charset="UTF-8">
    <title>Heya Servant!</title>
    <h1>User list:</h1>
    <div id="status"><!-- The request status --></div>
    <div id="userList">
      <!-- We'll fill this with the user information -->
    // Avoid clashing Node.js/Electron with jQuery as per
    window.nodeRequire = require;
    delete window.require;
    delete window.exports;
    delete window.module;
    // Fetch jQuery.
    window.$ = window.jQuery = nodeRequire('./resources/jQuery-2.2.3.min.js')
    // The JS file that will do the heavy lifting.

And finally we’ll implement the logic in renderer.js.

// Backend and endpoint details.
const host     = ''
const endpoint = '/users'
// Retry configuration.
let maxNoOfAttempts        = 50,
    waitTimeBetweenAttempt = 250

let _fetchUserList = function(waitTime, maxAttempts, currentAttemptNo) {
  $.getJSON(host + endpoint, function(users) {
    $('#status').html(`Fetched the content after attemt no.
    // Construct the user list HTML output
    let output = "";
    for (let i in users) {
      let user = users[i]
      output += `ID: ${user.userId},
                 Firstname: ${user.userFirstName},
                 Lastname: ${user.userLastName}
  }).fail(function() {
    $('#status').html(`Attempt no. <b>${currentAttemptNo}</b>. Are you sure the
                       server is running on <b>${host}</b>, and the endpoint
                       <b>${endpoint}</b> is correct?`)
    // Keep trying until we get an answer or reach the maximum number of retries.
    if (currentAttemptNo < maxAttempts) {
      setTimeout(function() {
        _fetchUserList(waitTime, maxAttempts, currentAttemptNo+1)
      }, waitTime)

// Convenience function for `_fetchUserList`.
let fetchUserList = function(waitTimeBetweenAttempt, maxNoOfAttempts) {
  _fetchUserList(waitTimeBetweenAttempt, maxNoOfAttempts, 1)

// Start trying to fetch the user list.
fetchUserList(waitTimeBetweenAttempt, maxNoOfAttempts)

We simply request the JSON data at, with $.getJSON(...), and display it if we received the data. If the request failed, we keep retrying until we either get a response or reach the maximum number of attempts (here set to 50 via maxNoOfAttempts).

The real purpose behind the retry will become apparent later on, when we might need to wait for the server to become available. Normally you will use a status endpoint that you are 100% sure is correct and not failing to check for the availability (inspired by the answer Mike from Wagon HQ gave here).

Launching the Haskell web server from Electron

Now to the interesting part, let’s try to launch the Haskell web server from inside of Electron.

First though, let us set the haskell-app/resources folder as the target for our binary, in the stack.yaml file, with the local-bin-path configuration value.

resolver: lts-5.15
local-bin-path: ../haskell-app/resources

Now let’s compile the executable.

In your shell/terminal
$ cd Haskell-Electron-app/backend
$ stack build --copy-bins

The --copy-bins (or alternatively you can just do stack install) will copy over the binary to Haskell-Electron-app/haskell-app/resources as we specified (it defaults to ~/.local/bin if local-bin-path is not set).

After that, it is surprisingly easy to launch the executable from within Electron (well, easy once you already know how). We will change main.js to spawn a process for the web server upon app initialization (i.e. the ready state). Since there are bits and pieces that are added I’ll include the whole file, with most of the comments removed.

const electron = require('electron')
// Used to spawn processes.
const child_process = require('child_process')
const app =
const BrowserWindow = electron.BrowserWindow

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow
// Do the same for the backend web server.
let backendServer

function createWindow () {
  mainWindow = new BrowserWindow({width: 800, height: 600})
  mainWindow.loadURL('file://' + __dirname + '/index.html')
  mainWindow.on('closed', function () {
    mainWindow = null

function createBackendServer () {
  backendServer = child_process.spawn('./resources/backend-exe')

app.on('ready', createWindow)
// Start the backend web server when Electron has finished initializing.
app.on('ready', createBackendServer)
// Close the server when the application is shut down.
app.on('will-quit', function() {
app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') {
app.on('activate', function () {
  if (mainWindow === null) {

We are using the child_process.spawn command to launch our backend web server. Let’s briefly go through what is happening:

  • Imported the child_process module with const child_process = require('child_process')
  • Defined a variable let backendServer that’ll let us keep the backend server from being garbage collected
  • Added a function createBackendServer that runs child_process.spawn('./resources/backend-exe') to spawn the process
  • Added the createBackendServer function to the ready hook with app.on('ready', createBackendServer)
  • Close the backendServer when the event will-quit occurs

And voila! We now have Electron spawning a process that runs a Haskell web server! :)

Next step would be to package the app up for distribution to see if that affects anything, but I’ll save that for another time (and Electron already has a page on distribution here).

Compiling SCSS and JavaScript in Hakyll

Versions used:

  • Hakyll
  • hjsmin
  • Sass 3.4.18

This seems to be an often asked question, so I thought I’d try and share the approach that I’ve arrived at after having explored a couple of solutions to the problem. If you want to see the full code in action, check out the repo for the site (linking to v1.0.0 is intended, in case the code changes later on).

Compiling and minifying JavaScript

For some reason Hakyll does not include its own JavaScript compiler, which makes little sense. Luckily there is a package called hjsmin giving us Text.Jasmine, which we will use to both compile and minify our JavaScript files.

NOTE: From personal experience an earlier version of hjsmin ( would throw a parse error on some files (like jQuery). This has later been fixed in, but unfortunately Stackage is using in the current LTS 5.15.

To get hjsmin working with stack, you can add the following to the stack.yaml file in the project.

extra-deps: [ hjsmin-
            , language-javascript-

and hjsmin == 0.2.* as a dependency in the projects cabal file.

Now we are ready to construct the compiler itself.

import qualified Data.ByteString.Lazy.Char8 as C
import           Text.Jasmine

-- | Create a JavaScript compiler that minifies the content
compressJsCompiler :: Compiler (Item String)
compressJsCompiler = do
  let minifyJS = C.unpack . minify . C.pack . itemBody
  s <- getResourceString
  return $ itemSetBody (minifyJS s) s

The code is fairly straightforward. We use the Text.Jasmine provided function minify which has the signature ByteString -> ByteString, meaning it takes in the JavaScript code as string, and produces the result to a string.

Later on inside the main Hakyll function, we use it simply as we would the other compilers.

-- | Define the rules for the site/hakyll compiler
main :: IO ()
main = hakyll $ do
  -- | Route for all JavaScript files found in the 'js' directory
  match "js/*" $ do
    route   idRoute
    compile compressJsCompiler
  -- The rest of your rules...

Compiling and minifying SCSS (Sass)

For those who aren’t aware, there are other ways to write CSS than CSS. Sass and SCSS adds a lot of niceties to CSS, such as nesting, variables and mixins, and compiles to normal CSS. You can read more about that on their website.

This time we rely on external dependencies, namely the sass tool, which can be installed with gem install sass.

NOTE: There is a library called hsass, which provides a Haskell interface, but I’ve been running into problems with linking to the underlying C API. As such, I’ve opted for the external dependency for now.
-- | Create a SCSS compiler that transpiles the SCSS to CSS and
-- minifies it (relying on the external 'sass' tool)
compressScssCompiler :: Compiler (Item String)
compressScssCompiler = do
  fmap (fmap compressCss) $
    >>= withItemBody (unixFilter "sass" [ "-s"
                                        , "--scss"
                                        , "--compass"
                                        , "--style", "compressed"
                                        , "--load-path", "scss"

This time we have no library dependencies, and use the Hakyll provided function unixFilter to call the sass tool. An important thing is the arguments that we pass, which I’ll explain briefly:

  • -s tells sass to take its input from stdin
  • --scss tells sass to use the SCSS format
  • --compass tells sass to make compass imports available
  • --style compressed tells sass to compress the output
  • --load-path scss tells sass to look for modules in the scss directory (if we import stuff)

Much like with the JavaScript compiler, we use it in the main Hakyll function as such:

-- | Define the rules for the site/hakyll compiler
main :: IO ()
main = hakyll $ do
  -- | Compile the SCSS, from 'scss/app.scss', to CSS and serve it as 'app.css'
  match "scss/app.scss" $ do
   route   $ constRoute "app.css"
   compile compressScssCompiler
  -- The rest of your rules...

You can now happily compile both JavaScript and SCSS in your Hakyll project, without much hassle :).