Some Triva about PureScript

PureScript is a purely functional language specifically targeting JavaScript as its backend. If you have tasted Haskell and lamenting about the current untyped mess in scripting languages, especially regarding web-programming, probabiy your only choice out there is PureScript.

PureScript is the only language with a sane approch to staticaly support duck typing on records by using Row Polymorphism.

In addition, PureScript supports Extensible Effects with the same mechanism, using Row Polymorphism. Monads are labeled with effects (e.g. console for console I/O) in PureScript. This greatly reduces the problem of excessive transformer stack-up when composing Haskell monadic libraries simply for combining effects in obvious ways.

Short note for new PureScript users (like me)

This is a short note for those who want to try PureScript, who are already familliar with GHC and cabal. PureScript is relatively well documented, but for newbies it could be confusing because updated documents for the recent version 0.7.1.0 are scatterred around several URLs and the e-book and several other documents on the web is written based on the older version.

Installing PureScript is easy if you have installed cabal for installing PureScript and npm for installing JavaScript packages, especially for installing pulp (package dependency manangment/sandboxing for PureScript). Since purescript is implemented in Haskell, I felt it most natural to use cabal because I am GHC user. But you don’t have to have GHC and cabal installed in fact. There are also binary distribtions directly downlodable or via other binary packge managers from the PureScript homepage; see http://www.purescript.org/download/ for more informtion. I’ll just stick to cabal in this document.

You can install PureScript in any system cabal runs.

cabal install purescript


For npm and pulp,

You’d do somthing like this on Mac OS X

brew install npm
npm install -g pulp


and on Debian-based linux systems

sudo apt-get install npm
npm install pulp


Most users of Mac OS X would have Admin rights so it is most reasonlable to install pulp globalally in the system. The PureScript binary files should be installed in ~/.cabal/bin and you should have already added ~/.cabal/bin to your PATH already if you are a sensible GHC user. Since you’ve installed pulp globally with -g option, it should also be in your PATH. Note that installing pulp can take a while, especially when you have freshly installed npm, because it installs all its dependencies.

Altough you can sudo install -g pulp on linux systems, if you are in sudo group, it is usually not recommended in linux systems. So, just install it locally and set up some additional path in bashrc or whatever shell configuration file you use. In some linux distros, such as Debian, the executable filename for the “node.js” is named as nodejs rather than node in most other systems. So, you’d have to make a symbolic link in one of your PATH anyway. The pulp package mananager for PureScript also tries to find node. Anyway this is what I did in Debian after installing pulp locally. The npm pacakge manager for JavaScript on Debian installs user packages into ~/npm_modules directory and its binary/executable files are symlinked into ~/npm_modules/.bin directory.

kyagrd@debair:~$ ls -al node_modules/
total 16
drwxr-xr-x  4 kyagrd kyagrd 4096 Aug  2 22:30 .
drwxr-xr-x 33 kyagrd kyagrd 4096 Aug  3 00:01 ..
drwxr-xr-x  2 kyagrd kyagrd 4096 Aug  3 00:00 .bin
drwxr-xr-x  4 kyagrd kyagrd 4096 Aug  2 22:29 pulp
kyagrd@debair:~$ ls -al node_modules/.bin
total 8
drwxr-xr-x 2 kyagrd kyagrd 4096 Aug  3 00:00 .
drwxr-xr-x 4 kyagrd kyagrd 4096 Aug  2 22:30 ..
lrwxrwxrwx 1 kyagrd kyagrd   15 Aug  3 00:00 node -> /usr/bin/nodejs
lrwxrwxrwx 1 kyagrd kyagrd   16 Aug  2 22:30 pulp -> ../pulp/index.js


So, this is a good place to stick in the renamed symlink for “node.js”. (If you don’t like it here you can put it in anywhere else say ~/bin and add it to your PATH.)

kyagrd@debair:~$ ln -s /usr/bin/nodejs ~/node_modules/.bin/node
kyagrd@debair:~$ ls -al node_modules/.bin
total 8
drwxr-xr-x 2 kyagrd kyagrd 4096 Aug  3 00:00 .
drwxr-xr-x 4 kyagrd kyagrd 4096 Aug  2 22:30 ..
lrwxrwxrwx 1 kyagrd kyagrd   15 Aug  3 00:00 node -> /usr/bin/nodejs
lrwxrwxrwx 1 kyagrd kyagrd   16 Aug  2 22:30 pulp -> ../pulp/index.js


Now we’re done with all the tedious stuff and have fun.

kyagrd@debair:~$ mkdir pursproj
kyagrd@debair:~$ cd pursproj/
kyagrd@debair:~/pursproj$ pulp init
* Generating project skeleton in /home/kyagrd/pursproj
bower cached        git://github.com/purescript/purescript-console.git#0.1.0    
bower validate      0.1.0 against git://github.com/purescript/purescript-console.git#^0.1.0                                                                     
bower cached        git://github.com/purescript/purescript-eff.git#0.1.0        
bower validate      0.1.0 against git://github.com/purescript/purescript-eff.git#^0.1.0                                                                         
bower cached        git://github.com/purescript/purescript-prelude.git#0.1.1    
bower validate      0.1.1 against git://github.com/purescript/purescript-prelude.git#^0.1.0
bower install       purescript-console#0.1.0
bower install       purescript-eff#0.1.0
bower install       purescript-prelude#0.1.1

purescript-console#0.1.0 bower_components/purescript-console
└── purescript-eff#0.1.0

purescript-eff#0.1.0 bower_components/purescript-eff
└── purescript-prelude#0.1.1

purescript-prelude#0.1.1 bower_components/purescript-prelude


The pulp init command creates a template PureScript project for you. To see other command of pulp, you can pulp --help.

Usage: /home/kyagrd/node_modules/.bin/pulp [options]  [command-options]

Global options:
  --bower-file -b  Read this bower.json file instead of autodetecting it.
  --help -h              Show this help message.
  --monochrome           Don't colourise log output.
  --then         Run a shell command after the operation finishes.
                         Useful with `--watch`.
  --version -v           Show current pulp version
  --watch -w             Watch source directories and re-run command if
                         something changes.

Commands:
  browserify Produce a deployable bundle using Browserify.
  build      Build the project.
  dep        Invoke Bower for package management.
  docs       Generate project documentation.
  init       Generate an example PureScript project.
  psci       Launch a PureScript REPL configured for the project.
  run        Compile and run the project.
  test       Run project tests.

  Use `/home/kyagrd/node_modules/.bin/pulp  --help` to learn about
  command specific options.


We can try run and test commands

kyagrd@debair:~/pursproj$ pulp run
* Building project in /home/kyagrd/pursproj
* Build successful.
Hello sailor!
kyagrd@debair:~/pursproj$ cat src/Main.purs 
module Main where

import Control.Monad.Eff.Console

main = do
  log "Hello sailor!"
kyagrd@debair:~/pursproj$ pulp test
* Building project in /home/kyagrd/pursproj
* Build successful. Running tests...
You should add some tests.
* Tests OK.
kyagrd@debair:~/pursproj$ cat test/Main.purs 
module Test.Main where

import Control.Monad.Eff.Console

main = do
  log "You should add some tests."
kyagrd@debair:~/pursproj$ 


The run and test command invokes build command before running or testing if the project has not been built yet.

One last thing is about editing the “bower.json” file. The pulp package manager for PureScript uses bower (a local sandboxing package manager for javascirpt packages and also used for purescript cuase purescript package seems to be desinged to be compatible with javascript package versioning/dpenecy convention) to locally sandbox all its dependencies in the bower_compnents directory. I think for beginners like me, the only thing to edit is the dependency section.

kyagrd@debair:~/pursproj$ ls
bower.json  bower_components  output  src  test
kyagrd@debair:~/pursproj$ cat bower.json 
{
  "name": "pursproj",
  "version": "1.0.0",
  "moduleType": [
    "node"
  ],
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "output"
  ],
  "dependencies": {
    "purescript-console": "^0.1.0"
  }
}


The dependices section of the default “bower.json” created by invoking pulp init only contains the purescript-console package. As you need to use more library packages, you can list them in the dependencies section. There is a list of most commonly used basic packages (of course including purescript-console) combined as a sort of meta-package called purescript-base. We can edit the depenecy section, instead of purescript-console change it to purescript-base. Because purescript-base depends on purescript-console, it is redundent to specify purescript-console when there is purescript-base. The current version of purescript-base is 0.1.0, which happens to be same as the version of purescript-console.

So, change it like this and then install all its depending packages using pulp dep install (which invokes bower install). It sure does install more packages than before.

kyagrd@debair:~/pursproj$ vi bower.json  ### edit with text editor
kyagrd@debair:~/pursproj$ cat bower.json 
{
  "name": "pursproj",
  "version": "1.0.0",
  "moduleType": [
    "node"
  ],
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "output"
  ],
  "dependencies": {
    "purescript-base": "^0.1.0"
  }
}
kyagrd@debair:~/pursproj$ pulp dep install 
bower cached        git://github.com/purescript-contrib/purescript-base.git#0.1.0
bower validate      0.1.0 against git://github.com/purescript-contrib/purescript-base.git#^0.1.0
bower cached        git://github.com/purescript/purescript-arrays.git#0.4.1
bower validate      0.4.1 against git://github.com/purescript/purescript-arrays.git#^0.4.0
bower cached        git://github.com/purescript/purescript-control.git#0.3.0
bower validate      0.3.0 against git://github.com/purescript/purescript-control.git#^0.3.0
bower cached        git://github.com/purescript/purescript-either.git#0.2.0
bower validate      0.2.0 against git://github.com/purescript/purescript-either.git#^0.2.0
bower cached        git://github.com/purescript/purescript-enums.git#0.5.0
bower validate      0.5.0 against git://github.com/purescript/purescript-enums.git#^0.5.0
bower cached        git://github.com/purescript/purescript-foldable-traversable.git#0.4.0
bower validate      0.4.0 against git://github.com/purescript/purescript-foldable-traversable.git#^0.4.0
bower cached        git://github.com/paf31/purescript-lists.git#0.7.0
bower validate      0.7.0 against git://github.com/paf31/purescript-lists.git#^0.7.0
bower cached        git://github.com/purescript/purescript-math.git#0.2.0
bower validate      0.2.0 against git://github.com/purescript/purescript-math.git#^0.2.0
bower cached        git://github.com/purescript/purescript-maybe.git#0.3.3
bower validate      0.3.3 against git://github.com/purescript/purescript-maybe.git#^0.3.0
bower cached        git://github.com/purescript/purescript-monoid.git#0.3.0
bower validate      0.3.0 against git://github.com/purescript/purescript-monoid.git#^0.3.0
bower cached        git://github.com/purescript/purescript-strings.git#0.5.5
bower validate      0.5.5 against git://github.com/purescript/purescript-strings.git#^0.5.0
bower cached        git://github.com/purescript/purescript-tuples.git#0.4.0
bower validate      0.4.0 against git://github.com/purescript/purescript-tuples.git#^0.4.0
bower cached        git://github.com/paf31/purescript-unfoldable.git#0.4.0
bower validate      0.4.0 against git://github.com/paf31/purescript-unfoldable.git#^0.4.0
bower cached        git://github.com/purescript/purescript-integers.git#0.2.1
bower validate      0.2.1 against git://github.com/purescript/purescript-integers.git#^0.2.0
bower cached        git://github.com/purescript/purescript-st.git#0.1.0
bower validate      0.1.0 against git://github.com/purescript/purescript-st.git#^0.1.0
bower cached        git://github.com/purescript-contrib/purescript-bifunctors.git#0.4.0
bower validate      0.4.0 against git://github.com/purescript-contrib/purescript-bifunctors.git#^0.4.0
bower cached        git://github.com/purescript/purescript-lazy.git#0.4.0
bower validate      0.4.0 against git://github.com/purescript/purescript-lazy.git#^0.4.0
bower cached        git://github.com/purescript/purescript-invariant.git#0.3.0
bower validate      0.3.0 against git://github.com/purescript/purescript-invariant.git#^0.3.0
bower install       purescript-base#0.1.0
bower install       purescript-arrays#0.4.1
bower install       purescript-either#0.2.0
bower install       purescript-control#0.3.0
bower install       purescript-enums#0.5.0
bower install       purescript-foldable-traversable#0.4.0
bower install       purescript-lists#0.7.0
bower install       purescript-math#0.2.0
bower install       purescript-maybe#0.3.3
bower install       purescript-monoid#0.3.0
bower install       purescript-strings#0.5.5
bower install       purescript-tuples#0.4.0
bower install       purescript-unfoldable#0.4.0
bower install       purescript-st#0.1.0
bower install       purescript-integers#0.2.1
bower install       purescript-bifunctors#0.4.0
bower install       purescript-lazy#0.4.0
bower install       purescript-invariant#0.3.0

purescript-base#0.1.0 bower_components/purescript-base
├── purescript-arrays#0.4.1
├── purescript-console#0.1.0
├── purescript-control#0.3.0
├── purescript-either#0.2.0
├── purescript-enums#0.5.0
├── purescript-foldable-traversable#0.4.0
├── purescript-integers#0.2.1
├── purescript-lists#0.7.0
├── purescript-math#0.2.0
├── purescript-maybe#0.3.3
├── purescript-monoid#0.3.0
├── purescript-strings#0.5.5
├── purescript-tuples#0.4.0
└── purescript-unfoldable#0.4.0

purescript-arrays#0.4.1 bower_components/purescript-arrays
├── purescript-foldable-traversable#0.4.0
├── purescript-st#0.1.0
└── purescript-tuples#0.4.0

purescript-either#0.2.0 bower_components/purescript-either
└── purescript-foldable-traversable#0.4.0

purescript-control#0.3.0 bower_components/purescript-control
└── purescript-prelude#0.1.1

purescript-enums#0.5.0 bower_components/purescript-enums
├── purescript-either#0.2.0
├── purescript-strings#0.5.5
└── purescript-unfoldable#0.4.0

purescript-foldable-traversable#0.4.0 bower_components/purescript-foldable-traversable
├── purescript-bifunctors#0.4.0
└── purescript-maybe#0.3.3

purescript-lists#0.7.0 bower_components/purescript-lists
├── purescript-foldable-traversable#0.4.0
├── purescript-lazy#0.4.0
└── purescript-unfoldable#0.4.0

purescript-math#0.2.0 bower_components/purescript-math

purescript-maybe#0.3.3 bower_components/purescript-maybe
└── purescript-monoid#0.3.0

purescript-monoid#0.3.0 bower_components/purescript-monoid
├── purescript-control#0.3.0
└── purescript-invariant#0.3.0

purescript-strings#0.5.5 bower_components/purescript-strings
└── purescript-maybe#0.3.3

purescript-tuples#0.4.0 bower_components/purescript-tuples
└── purescript-foldable-traversable#0.4.0

purescript-unfoldable#0.4.0 bower_components/purescript-unfoldable
├── purescript-arrays#0.4.1
└── purescript-tuples#0.4.0

purescript-st#0.1.0 bower_components/purescript-st
└── purescript-eff#0.1.0

purescript-integers#0.2.1 bower_components/purescript-integers
├── purescript-math#0.2.0
└── purescript-maybe#0.3.3

purescript-bifunctors#0.4.0 bower_components/purescript-bifunctors
└── purescript-control#0.3.0

purescript-lazy#0.4.0 bower_components/purescript-lazy
└── purescript-monoid#0.3.0

purescript-invariant#0.3.0 bower_components/purescript-invariant
└── purescript-prelude#0.1.1


There are several tutorial examples online. Many of them should be doable by editing the src/Main.purs file. Ones that actually runs on browsers would need to use pulp browserify, which outputs single file that contains self contained javascript code. We can do it for this template project too (you need to check developer console for the output though).

kyagrd@debair:~/pursproj$ pulp browserify > index.js                           
* Browserifying project in /home/kyagrd/pursproj
* Building project in /home/kyagrd/pursproj
* Build successful.
* Browserifying...
kyagrd@debair:~/pursproj$ cat > index.html
<html>
 <head>
  <title>Example purescript app</title>
  <meta charset="UTF-8">
 </head>
 <body>
  Check the console
  <script type="text/javascript" src="index.js"></script>
 </body>
</html>
kyagrd@debair:~/pursproj$ ls
bower.json  bower_components  index.html  index.js  output  src  test


Load “index.html” in a browser that supports a deveolper console like Firefox. Every time you load “index.html” you can see the console log printing “hello sailor!”, just as it did when we pulp run on the command line console.

Happy PureScript-ing!