Haskell on AWS Lambda

This post will demonstrate running Haskell on AWS Lambda.

Background

AWS allows running arbitrary binaries https://aws.amazon.com/blogs/compute/running-executables-in-aws-lambda/ which we can use to write Lambda functions using Haskell.

Prerequisites

  1. Download and install Docker
  2. Download and install Cabal

Hello World

Create a hello world Haskell binary.

The binary needs to be compiled for x86_64 / AWS Linux, see https://aws.amazon.com/blogs/compute/running-executables-in-aws-lambda/.

mkdir -p lambda-test/src

file lambda-test/lambda-test.cabal:

-- Initial lamda-test.cabal generated by cabal init.  For further
-- documentation, see http://haskell.org/cabal/users-guide/
name:                lambda-test
version:             0.1.0.0
-- synopsis:           
-- description:        
-- license:            
license-file:        LICENSE
author:              Adam Evans
maintainer:          aevans@atlassian.com
-- copyright:          
-- category:           
build-type:          Simple
-- extra-source-files: 
cabal-version:       >=1.10
executable lambda-test
  ghc-options:         -Wall
  hs-source-dirs:      src
  main-is:             Main.hs
  build-depends:       base >=4.8 && <4.9
  default-language:    Haskell2010

file lambda-test/Main.hs:

module Main where
 
main = putStrLn "Hello World Haskell"

Build the binary

I use Mac OS so need to build the binary using Docker to be compatible with the Lambda environment. If you develop on a Linux machine you can probably skip using Docker to build the binary.

cd lambda-test

docker run --rm -v "$PWD":/tmp/lambda -w /tmp/lambda thoughtbot/ghc \
  /bin/bash -c "cabal sandbox init; cabal build"

Shim file

We will use the Lambda native Node.js support as a shim to invoke the Haskell executable.

file Main.js:

process.env['PATH'] = process.env['PATH'] + ':' + process.env['LAMBDA_TASK_ROOT'];
var exec = require('child_process').execFile;
exports.handler = function(event, context) {
    exec('./lambda-test', function(err, data) {
        console.log(err);
        console.log(data.toString());
        context.done(null, data.toString());
    });
};

Package the Lambda

Note -j strips the directory path.

zip -r -j lambda-test.zip Main.js dist/build/lambda-test/lambda-test

Create the Lambda function

In the AWS console go to the Lambda section, click “Create Lambda Function” Create Labda Function

On the Select blueprint page, click Skip at the bottom. Select blueprint

Configure

On the Configure page make sure Runtime is set to Node.js.

On code entry type select “Upload a .ZIP file” and upload the zip we created

On “Handler” change this to “Main.handler” to match the name of our Main.js file. Configure function

Test

After saving the function you can test it by clicking the “Test” button Test function

You should see the below:

Extending

The event and context data passed to the Node.js handler function can be passed on to the Haskell executable as command line arguments.