Purely functional programming languages have no place in the browser and Haskell is a magical genius language that everyone talks about but no one really uses.

Truth is, haskell is not a difficult language to program in and it’s past reputation of never being used in the industry has always been uncalled for.
As for how we can use it to program applications for the web, there are some really coolprojects that make this possible and this post will show you just that.

This will be a hands on tutorial on how to use Haste, a Haskell to Javascript compiler that allows you to write Haskell code that can be executed on the web. We will be creating a simple pong game while exploring some of the features of the Haste environment. You can check the game out over here.

This tutorial assumes that you have basic knowledge of haskell or some other functional programming language. It also uses version 0.4 of the Haste compiler so you should choose to install this version if you plan to try out the code along. Or you may want to follow this code instead ported for version 0.5.

Install Haste on your machine to get started. It requires GHC, the Glasgow Haskell Compiler so you want to install that first if you don’t have it already.

That aside, let’s begin our pong game.

Rules of the game

There are two paddles and one ball.

When the ball hits any of the walls (right, left) or paddles, it should change its direction.

When a ball hits the ceiling or floor, the game should end.

Paddles should be controlled by the player’s mouse. (Not really a rule of the game but hey!)

First, we import some useful functions and datatypes from the Haste libraries. Create a file called pong.hs and add the following statements to the top of the file.

importHasteimportHaste.Graphics.CanvasimportData.IORef

Step 1 - Defining Our Game state and Initial Declarations

We start by declaring a datatype GameState. We’ll use it to describe the state of our game at any given moment, using values like the ball and paddle position, current score, speed of the ball. Add this code to pong.hs.

dataGameState=GameState{ballPos::Point,-- position of ballballSpeed::Point,-- how far will ball move in a single updatepaddlePos::Double,-- start position of paddle on x axisscore::Int}

Note that the paddlePos field has a single value. You might expect that we need to store the full dimensions of the paddles (x and y coordinates) but as you will see very soon, the other values never change and so are declared as constants instead of passing them around in our game state.

Now we define constants to be used throughout the program. These include the dimensions of the canvas and partial dimensions of paddles, radius of the ball etc.

width,height,ballRadius,paddleWidth,paddleHeight::Doublewidth=500-- width of canvasheight=600-- height of canvasballRadius=5--radius of ballpaddleHeight=5-- height of paddlepaddleWidth=150-- width of paddlehalfWidth=width/2-- well, half the widthhalfHeight=height/2--also half the heightscoreLabel::StringscoreLabel="Score: "

Also define a state describing the initial values of our game.

initialState::GameStateinitialState=GameState{ballPos=(20,20),ballSpeed=(8,10),paddlePos=(width/2)-75,--position around center of canvasscore=0}

Step 2 - Canvas, Paddles and Ball

Our animation is going to be on an HTML canvas but we can make life easier by abstracting the process of creating a canvas. We define a function mkCanvasmkCanvas :: Double -> Double -> IO Elem
The type signature tells us in a nutshell that we give it two doubles (width and height of our canvas) and receive instructions for creating a HTML element as per Haskell monads. This is the body of the function.

This function creates a new canvas html element using the newElem function from the Haste.Graphics.Canvas library, sets its dimensions (height, width) and assigns some other housekeeping properties like color, border etc.

This library also gives us functions for creating basic shapes and pictures. Here are some we will be using

circle::Point->Double->Shape()-- draw a circle at given Point with given radiusrect::Point->Point->Shape()-- draw a rectangle between two pointscolor::Color->Picture()->Picture()-- draw the given picture using the specified color

A Point is simply a pair of floats which may or may not represent x and y coordinates in pixels

typePoint=(Double,Double)

Now back in our game, we can use the color function to define our color theme.

white::Picture()->Picture()white=color(RGB255255255)-- or whichever color you like

For our ball and paddles, we use a familiar technique of abstracting the creation process using factory functions. Add this to pong.hs.

All three functions are very similar to each other. Their type signature tells us they receive a set of values and return a Picture monad Picture(), instructions for drawing their respective shapes on a canvas. the drawText function writes text on the canvas.

Step 3 - Drawing on the canvas

At this point we have functions for creating our paddles, ball and canvas. It’s about time that we actually use them to well, create paddles, ball and a canvas.
We want to draw a picture of our game on the canvas and to do that we first need a picture of our game. This is where our GameState type comes into play (Pun intended! 😒).

gamePicture::GameState->Picture()gamePicturestate=doball$ballPosstate-- ball position from `state`letx1=paddlePosstate-- paddle start positionx2=x1+paddleWidth-- end position of paddlepaddle$Rectx10x2paddleHeight-- top paddlepaddle$Rectx1(height-paddleHeight)x2height-- bottom paddlefont"20px italic Monospace"$drawText(30,50)$scoreLabel++show(scorestate)-- write the score onto the canvas

The gamePicture function takes as an argument, a GameState and returns a Picture monad. So it basically gives you a picture based on our given game state which we can the render on our canvas. So what’s going on here?

In a do block, we create 4 pictures (Picture monads to be technical) to draw on the canvas.
1. The ball. Using the ballPos field of the given state as our argument to the ball function.
2. and 3. The top and bottom paddles. Using the paddle function and their coordinates. Notice that we only needed the paddle’s x coordinate from the state, the rest can be inferred from the paddle’s orientation (Top or Bottom). The top paddle starts at height 0 while the bottom baddle starts right before the end of the canvas (height - paddleHeight).
4. The text field showing the score on the canvas using the drawText function.

While gamePicture produces the picture, the renderState function will do the actual rendering onto a canvas.

The render function is imported from haste and draws a given picture (or series of pictures using a do block) on the specified canvas.

We move on to (finally 😁) create our canvas.
We’ll create the canvas inside our main function. The main function main :: IO () in haskell, unlike any other function has to be called main and is the entry point of our program just like in C, Java etc.

At this point you can check that the screen is drawn as it should. Compile the program by running this command in a terminal.

$ hastec --output-html pong.hs

Haste automagically creates an HTML file called pong.html with the javascript version of our code embedded. You may omit the --output-html option if you just want the javascript file. Open the html file in a web browser and you should see a canvas, two paddles, a ball and a score card. But they are static and that’s because we havent added any animation to them yet. We do that next.

Step 4 - Animation

We continue with ball animation. The ballSpeed field of our GameState type is useful for this purpose. Simply put, everytime the screen is redrawn, we want the ball to change its position on the canvas. Incrementing the x and y coordinates of the ball by a value everytime gives us this effect and these values are what we have as ballSpeed in our GameState.

moveBall::GameState->GameStatemoveBallstate=state{ballPos=(x+vx,y+vy)}--increment by vx and vywhere(x,y)=ballPosstate(vx,vy)=ballSpeedstate

moveBall increments the x and y coordinates of the ball and returns a new GameState with the new coordinates. Note that there is no mutation/side effects here as the function does indeed return a new value.

Our paddles will be controlled by the mouse so we need to listen for mouse events, specifically the mousemove event.
To add an event listener to our game, we go back to our main function.
Now REMOVE the last statement renderState canvas initialState from the do block as we will no longer be needing it. Instead, add the following statements to the do block.

Remember that thing about Haskell being a purely functional language? Whoever said that didn’t finish the entire story it seems. Variables are immutable in Haskell but there are a few ways to create references whereby we can change what the reference points to.

We won’t be able to do a lot without interacting with the real world now that we need to process mouse events from the user, so we use the IORef data type to do the job. This is the first sighting of mutation in our code but don’t be alarmed if you haven’t seen this before. Data.IORef makes it quite easy and safe to do this.
The first statement creates a reference object of type IORef GameState using the newIORef function. This creates a new GameState reference with our initialState, allowing us to modify its contents throughout our code.
The second statement adds a mousemove event to the canvas element. The onEvent function provided by Haste takes an Elem and an Event while Event constructor take as arguments a callback function à la Javascript. Our callback function \mousePos -> do movePaddles mousePos stateRef receives the mouse position mousePos and moves the paddles whenever the mousemove event fires.
Here is the code to move our paddles.

readIORef extracts the GameState referenced by our state reference stateRef while atomicModifyIORef changes the referenced content using the extracted state. The atomicModifyIORef function unlike the modifyIORef mutates the variable atomically, preventing race conditions and the likes. Our state is simply updated by centering the position of the paddle around the mouse coordinates

Now we move on to define our primary animation function. Let’s call it animate. It takes a Canvas, an IORef GameState (for rendering onto canvas) and returns an IO monad IO () .

animate::Canvas->IORefGameState->IO()animatecanvasstateRef=dostate<-readIORefstateRef-- extract state from reference objectrenderStatecanvasstate-- draw game pictureatomicWriteIORefstateRef$updatestate-- update state and rewrite state referencesetTimeout30$animatecanvasstateRef-- sleep. then loopwhereupdate=moveBall

animate updates that state with the update function, writes the updated state back to the state variable then waits 30 milliseconds before repeating the whole process so it runs in a loop.
Later on we’ll add a few more functions used to compose the update function but for now it consists only of the moveBall. atomicWriteIORef is similar to atomicModifyIORef but overwrites the variable with a new state. This feels more efficient here since we can extract our pure state, pass it around various functions composed by the update function and then, only once do we need to commit the sinful act of changing the value referenced by our IORef variable 😇.

Now add the following as the last line of our main function
animate canvas stateRef

Step 5 - Detecting Collision

We’ve got our ball and paddles moving and what’s left is making the ball bounce.
The rules of our game requires the ball to bounce when it hits any of the walls or paddles.
Let’s start with the paddles. Here’s our function paddleHit to detect collision between the ball and the paddle

paddleHit::GameState->GameStatepaddleHitstate=ifand[bx'>=px,bx''<=pl,(by>=height-ph)||(by<=ph)]-- if ball is touching paddlethenstate{ballSpeed=(vx,-vy),score=scorestate+1}-- change ball direction and increase scoreelsestate-- otherwise do nothingwhere(bx,by)=ballPosstate-- x and y coordinates of ballbx'=bx+ballRadius-- right edge of ballbx''=bx-ballRadius-- left edge of ball(vx,vy)=ballSpeedstate-- current ball speedpx=paddlePosstate-- x coordinate of paddle / left edgeph=paddleHeight-- ph is easier to typepl=px+paddleWidth-- right edge of paddle

paddleHit checks if the ball coordinates are within the dimensions of the paddles and if so, returns a new GameState wherein the ball now heads in the opposite direction. This is accomplished by simply negating the vertical speed value of the ball. We also increment the score for each time the ball hits the paddle.

Note that for the paddleHit function, the and function short-circuit evaluates, so once an expression returns false, we simply return our state unchanged.
Detecting collision with walls is similar to the paddles.

We simply check if the ball crosses the right or left wall and if so, negate the horizontal speed of the ball.

Now we have our functions to detect collision with the walls and paddles. Let’s use them to compose our update function. Go to the animate function. Currently the update function defined within it consists only of moveBall so add our two new functions to it by replacing the definition of update with this line of code.

update=moveBall.paddleHit.wallCollision

The last rule of the game to be implemented is that the game should end when the ball hits the ceiling or the floor. That means we also need to check for ball collisions with the ceiling and floor of our canvas. Add this function to the code.

gameEnded::GameState->BoolgameEndedstate|y>=height&&(x<px||x>px+paddleWidth)=True-- if ball reaches floor and not touching paddle|y<=0&&(x<px||x>px+paddleWidth)=True-- if ball reaches ceiling and not touching paddle|otherwise=Falsewhere(x,y)=ballPosstate-- ball positionpx=paddlePosstate-- paddle position

gameEnded returns a Bool telling us if the game should end. That is, if the ball has collided with any of the vertical boundaries that isn’t the paddle. To make use of this function, we go back to our animate function and make our decision on whether to stop animating or not, based on the reply from gameEnded. Our animate function should now look like this

animate::Canvas->IORefGameState->IO()animatecanvasstateRef=dostate<-readIORefstateRefrenderStatecanvasstate-- draw game pictureifgameEndedstatethenrenderStatecanvasstate-- render one last time and quitelsedoatomicWriteIORefstateRef$updatestate-- else update state setTimeout30$animatecanvasstateRef-- and continuewhereupdate=moveBall.paddleHit.wallCollision

Wow! this is a really lengthy tutorial 😵. We were bound to reach this part at some point and good news is you now have written a game of pong in haskell.
Personally I’m a huge fan of Javascript as well so one thing I think is really cool about Haste is that you still get that Javascripty feeling, programming in it. Haste doesn’t take that away from you by forcing you to do things like DOM manipulation or event listening in a different way.

You may have seen that I included some extra features in the live demo such as start, restart buttons and speed of the ball increasing during the game. Browse through the code to see how these were implemented, then try to implement them and add more features to your game or fork this one on github and continue from there. If you get stuck in implementation somewhere, leave me a message/comment and I’ll be sure to help you as much as I can.