Getting Started

Chapter Goals

In this chapter, the goal will be to set up a working PureScript development environment and to write our first PureScript program.

Our first project will be a very simple PureScript library, which will provide a single function that can compute the length of the diagonal in a right-angled triangle.

Introduction

Here are the tools we will be using to set up our PureScript development environment:

  • purs - The PureScript compiler itself.
  • npm - The Node Package Manager, which will allow us to install the rest of our development tools.
  • spago - A command-line tool that automates many of the tasks associated with managing PureScript projects.

The rest of the chapter will guide you through installing and configuring these tools.

Installing PureScript

You will need to install Node.js and npm. We recommend installing Node.js and npm via a "node version manager" to avoid issues with installing packages gloablly. If you choose to install it manually, you might experience the EACCES error when installing packages globally.

Install the Purescript compiler (purs) with npm:

npm install -g purescript

Try running the PureScript compiler on the command line to verify that the PureScript compiler executables are available on your path:

$ purs

Installing Tools

Spago is a PureScript build tool and package manager. You will need to install it using npm, as follows:

$ npm install -g spago

This will place the spago command-line tools on your path. At this point, you will have all the necessary tools to create your first PureScript project.

Hello, PureScript!

Let's start out simple. We'll use Spago to compile and run a simple Hello World! program.

Begin by creating a project in an empty directory and use the spago init command to initialize the project:

$ mkdir my-project
$ cd my-project
$ spago init
Initializing a sample project or migrating an existing one..

$ ls
packages.dhall	spago.dhall	src		test

Spago has created a number of directories and configuration files. The src directory will contain our source files and the test directory will contain any tests we write. The spago.dhall file contains the project configuration.

Modify the src/Main.purs file to contain the following content:

module Main where

import Effect.Console

main = log "Hello, World!"

This small sample illustrates a few key ideas:

  • Every file begins with a module header. A module name consists of one or more capitalized words separated by dots. In this case, only a single word is used, but My.First.Module would be an equally valid module name.
  • Modules are imported using their full names, including dots to separate the parts of the module name. Here, we import the Effect.Console module, which provides the log function.
  • The main program is defined as a function application. In PureScript, function application is indicated with whitespace separating the function name from its arguments.

Let's build and run this code using the following command:

$ spago run
...
Build succeeded.
Hello, World!

Congratulations! You just compiled and executed your first PureScript program.

Compiling for the Browser

Spago can be used to turn our PureScript code into JavaScript suitable for use in the web browser by using the spago bundle-app command:

$ spago bundle-app
...
Build succeeded.
Bundle succeeded and output file to index.js

All the code in the src directory, a standard PureScript library known as the Prelude and any project dependencies have been compiled to JavaScript. The resulting code is bundled as index.js and has also had any unused code removed, a process known as dead-code-elimination. This index.js file can now be included in an HTML document. If you try this, you should see the words "Hello, World!" printed to your browser's console.

If you open index.js, you should see a few compiled modules which look like this:

// Generated by purs bundle 0.13.3
var PS = {};
(function(exports) {
  "use strict";

  exports.log = function (s) {
    return function () {
      console.log(s);
      return {};
    };
  };
})(PS["Effect.Console"] = PS["Effect.Console"] || {});
(function($PS) {
  // Generated by purs version 0.13.3
  "use strict";
  $PS["Effect.Console"] = $PS["Effect.Console"] || {};
  var exports = $PS["Effect.Console"];
  var $foreign = $PS["Effect.Console"];
  exports["log"] = $foreign.log;
})(PS);
(function($PS) {
  // Generated by purs version 0.13.3
  "use strict";
  $PS["Main"] = $PS["Main"] || {};
  var exports = $PS["Main"];
  var Effect_Console = $PS["Effect.Console"];
  var main = Effect_Console.log("Hello, World!");
  exports["main"] = main;
})(PS);
PS["Main"].main();

This illustrates a few points about the way the PureScript compiler generates JavaScript code:

  • Every module gets turned into an object, created by a wrapper function, which contains the module's exported members.
  • PureScript tries to preserve the names of variables wherever possible.
  • Function applications in PureScript get turned into function applications in JavaScript.
  • The main method is run after all modules have been defined and is generated as a simple method call with no arguments.
  • PureScript code does not rely on any runtime libraries. All of the code that is generated by the compiler originated in a PureScript module somewhere which your code depended on.

These points are important since they mean that PureScript generates simple, understandable code. The code generation process, in general, is quite a shallow transformation. It takes relatively little understanding of the language to predict what JavaScript code will be generated for a particular input.

Compiling CommonJS Modules

Spago can also be used to generate CommonJS modules from PureScript code. This can be useful when using NodeJS, or just when developing a larger project which uses CommonJS modules to break code into smaller components.

To build CommonJS modules, use the spago build command:

$ spago build
...
Build succeeded.

The generated modules will be placed in the output directory by default. Each PureScript module will be compiled to its own CommonJS module, in its own subdirectory.

Tracking Dependencies

To write the diagonal function (the goal of this chapter), we will need to be able to compute square roots. The math package contains type definitions for functions defined on the JavaScript Math object, so let's install it:

$ spago install math

The math library sources should now be available in the .spago/math/{version}/ subdirectory, and will be included when you compile your project.

Computing Diagonals

Let's write the diagonal function, which will be an example of using a function from an external library.

First, import the Math module by adding the following line at the top of the src/Main.purs file:

import Math (sqrt)

It's also necessary to import the Prelude module, which defines very basic operations such as numeric addition and multiplication:

import Prelude

Now define the diagonal function as follows:

diagonal w h = sqrt (w * w + h * h)

Note that there is no need to define a type for our function. The compiler can infer that diagonal is a function that takes two numbers and returns a number. In general, however, it is a good practice to provide type annotations as a form of documentation.

Let's also modify the main function to use the new diagonal function:

main = logShow (diagonal 3.0 4.0)

Now compile and run the project again, using spago run:

$ spago run
...
Build succeeded.
5.0

Testing Code Using the Interactive Mode

The PureScript compiler also ships with an interactive REPL called PSCi. This can be very useful for testing your code and experimenting with new ideas. Let's use PSCi to test the diagonal function.

Spago can load source modules into PSCi automatically, via the spago repl command:

$ spago repl
>

You can type :? to see a list of commands:

> :?
The following commands are available:

    :?                        Show this help menu
    :quit                     Quit PSCi
    :reload                   Reload all imported modules while discarding bindings
    :clear                    Discard all imported modules and declared bindings
    :browse      <module>     See all functions in <module>
    :type        <expr>       Show the type of <expr>
    :kind        <type>       Show the kind of <type>
    :show        import       Show all imported modules
    :show        loaded       Show all loaded modules
    :show        print        Show the repl's current printing function
    :paste       paste        Enter multiple lines, terminated by ^D
    :complete    <prefix>     Show completions for <prefix> as if pressing tab
    :print       <fn>         Set the repl's printing function to <fn> (which must be fully qualified)

By pressing the Tab key, you should be able to see a list of all functions available in your own code, as well as any project dependencies and the Prelude modules.

Start by importing the Prelude module:

> import Prelude

Try evaluating a few expressions now:

> 1 + 2
3

> "Hello, " <> "World!"
"Hello, World!"

Let's try out our new diagonal function in PSCi:

> import Main
> diagonal 5.0 12.0

13.0

You can also use PSCi to define functions:

> double x = x * 2

> double 10
20

Don't worry if the syntax of these examples is unclear right now - it will make more sense as you read through the book.

Finally, you can check the type of an expression by using the :type command:

> :type true
Boolean

> :type [1, 2, 3]
Array Int

Try out the interactive mode now. If you get stuck at any point, simply use the Clear command :clear to unload any modules which may be compiled in memory.

Exercises

  1. (Easy) Use the pi constant, which is defined in the Math module, to write a function circleArea which computes the area of a circle with a given radius. Test your function using PSCi (Hint: don't forget to import pi by modifying the import Math statement).
  2. (Medium) Use spago install to install the globals package as a dependency. Test out its functions in PSCi (Hint: you can use the :browse command in PSCi to browse the contents of a module; try :browse Global and compare what PSCi tells you to what you find in Pursuit).

Conclusion

In this chapter, we set up a simple PureScript project using the Spago tool.

We also wrote our first PureScript function and a JavaScript program which could be compiled and executed either in the browser or in NodeJS.

We will use this development setup in the following chapters to compile, debug and test our code, so you should make sure that you are comfortable with the tools and techniques involved.