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 which can compute the length of the diagonal in a right-angled triangle.
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.- Pulp - A command-line tool which automates many of the tasks associated with managing PureScript projects.
The rest of the chapter will guide you through installing and configuring these tools.
The recommended approach to installing the PureScript compiler is to download a binary release for your platform from the PureScript website.
You should verify that the PureScript compiler executables are available on your path. Try running the PureScript compiler on the command line to verify this:
$ purs
Other options for installing the PureScript compiler include:
- Via NPM:
npm install -g purescript
. - Building the compiler from source. Instructions can be found on the PureScript website.
If you do not have a working installation of NodeJS, you should install it. This should also install the npm
package manager on your system. Make sure you have npm
installed and available on your path.
You will also need to install the Pulp command line tool, and the Bower package manager using npm
, as follows:
$ npm install -g pulp bower
This will place the pulp
and bower
command line tools on your path. At this point, you will have all the tools needed to create your first PureScript project.
Let's start out simple. We'll use Pulp to compile and run a simple Hello World! program.
Begin by creating a project in an empty directory, using the pulp init
command:
$ mkdir my-project
$ cd my-project
$ pulp init
* Generating project skeleton in ~/my-project
$ ls
bower.json src test
Pulp has created two directories, src
and test
, and a bower.json
configuration file for us. The src
directory will contain our source files, and the test
directory will contain our tests. We will use the test
directory later in the book.
Modify the src/Main.purs
file to contain the following content:
module Main where
import Control.Monad.Eff.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
Control.Monad.Eff.Console
module, which provides thelog
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:
$ pulp run
* Building project in ~/my-project
* Build successful.
Hello, World!
Congratulations! You just compiled and executed your first PureScript program.
Pulp can be used to turn our PureScript code into Javascript suitable for use in the web browser, by using the pulp browserify
command:
$ pulp browserify
* Browserifying project in ~/my-project
* Building project in ~/my-project
* Build successful.
* Browserifying...
Following this, you should see a large amount of Javascript code printed to the console. This is the output of the Browserify tool, applied to a standard PureScript library called the Prelude, as well as the code in the src
directory. This Javascript code can be saved to a file, and included in a HTML document. If you try this, you should see the words "Hello, World!" printed to your browser's console.
Pulp provides an alternative command, pulp build
, which can be used with the -O
option to apply dead code elimination, which removes unnecessary Javascript from the output. The result is much smaller:
$ pulp build -O --to output.js
* Building project in ~/my-project
* Build successful.
* Bundling Javascript...
* Bundled.
Again, the generated code can be used in a HTML document. If you open output.js
, you should see a few compiled modules which look like this:
(function(exports) {
"use strict";
var Control_Monad_Eff_Console = PS["Control.Monad.Eff.Console"];
var main = Control_Monad_Eff_Console.log("Hello, World!");
exports["main"] = main;
})(PS["Main"] = PS["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. In fact, 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.
Pulp 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 pulp build
command (without the -O
option):
$ pulp build
* Building project in ~/my-project
* Build successful.
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.
To write the diagonal
function (the goal of this chapter), we will need to be able to compute square roots. The purescript-math
package contains type definitions for functions defined on the JavaScript Math
object, so let's install it:
$ bower install purescript-math --save
The --save
option causes the dependency to be added to the bower.json
configuration file.
The purescript-math
library sources should now be available in the bower_components
subdirectory, and will be included when you compile your project.
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 is able to infer that diagonal
is a function which 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 pulp run
:
$ pulp run
* Building project in ~/my-project
* Build successful.
5.0
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.
Pulp can load source modules into PSCi automatically, via the pulp repl
command:
$ pulp repl
>
You can type :?
to see a list of commands:
> :?
The following commands are available:
:? Show this help menu
:quit Quit PSCi
:reset Reset
:browse <module> Browse <module>
:type <expr> Show the type of <expr>
:kind <type> Show the kind of <type>
:show import Show imported modules
:show loaded Show loaded modules
:paste paste Enter multiple lines, terminated by ^D
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 Bower 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 Reset command :reset
to unload any modules which may be compiled in memory.
X> ## Exercises
X>
X> 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).
X> 1. (Medium) Use bower install
to install the purescript-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).
In this chapter, we set up a simple PureScript project using the Pulp 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.