Stack Builders News

A collection of thoughts and notes by our team

Eric C. Jones

A Look at PureScript - Part I


We needn't preface this discussion with the deficiencies of JavaScript. The sheer number of "Compile-To-JavaScript" languages is evidence that the language has problems and that many desire a suitable alternative. Yet a good replacement should possess a certain set of qualities with which we can justify the added overhead of compilation and integration with existing tools. Here we are going to look at PureScript, a small strongly, statically typed language which compiles to JavaScript1, and discuss why it is an excellent substitute.

module Example where

import Debug.Trace

main = trace "Hello, PureScript!"

JavaScript's syntax has grown quite legacy and fails to elegantly handle many modern programming constructs. To truly add value any potential replacement language must ease this syntactic burden. PureScript does a fine job of this by offering an ML style syntax which is very heavily inspired by Haskell. With PureScript we get clean and simple syntax for modern constructs such higher-order functions which fit snugly within existing JavaScript idioms.

module Example where

import Debug.Trace

main = giveMeATracingFunction trace

giveMeATracingFunction fn = fn "Hello, PureScript!"

Interoperation with existing JavaScript code and tools is another area where PureScript excels. PureScript has a dead simple FFI and compiles to genuinely legible JavaScript that can be easily inspected and debugged using modern browsers or existing JavaScript analysis tools. Furthermore, basic constructs such as records and arrays directly correspond to their JavaScript counterparts which facilitate the passing of data between the two worlds. For additional convenience and clarity, compiled PureScript modules export their public content using the revealing module pattern2, the functions of which can easily be called from JavaScript. If all of that isn't enough, the executable psc_make compiles PureScript into true CommonJS modules for easy integration into existing projects.

module Example where

import Control.Monad.Eff
import Debug.Trace

foreign import traceFn
  "function traceFn(statement) { \
  \  console.lo(statement); \
  \}" :: String -> Eff (trace :: Trace) {}

main = giveMeATracingFunction traceFn

giveMeATracingFunction fn = fn "Hello, PureScript!"
fig 1. Error in interfaced FFI JavaScript

The astute reader might have caught the error in our code: console.lo should in fact be console.log. Unlike PureScript, JavaScript allows simple typos to turn into run-time errors. One-hundred percent test coverage would surely catch mistakes like this but why rely on super-human testing capability when PureScript can detect a whole class of errors automatically and for free! Also, notice that PureScript does a good job of preserving the function names we have chosen. This, coupled with the clean and readable JavaScript that PureScript produces, gives us the ability to immediately identify the location of our JavaScript errors in the original source.

Syntax amenable to modern programming idioms and easy integration with existing JavaScript are both wonderfully important characteristics but as "real world" developers we choose only those tools which give us the ability to produce quality code faster and with more consistency. PureScript's close relationship with Haskell really shines in this respect. The powerful type system catches many errors at compile time that would otherwise be hidden until uncovered by well written tests or by discovery of a defect in the running application. Also, PureScript comes with an interpreter which can be used for quick exploration of ideas and the evaluation independent source files, this creates a tight feedback loop which speeds time to production.

Do You Have What it Takes To Be a Stack Builder?