Concepts of programming languages
PureScript
Christian Stuart, Douwe van Gijn, Martijn Fleuren and
Nick Begg
[Faculty of Science
Information and Computing
Sciences]
1
Presentation overview
▶ Part 1: Introduction and Practical Matters
▶ Part 2: More Language Detail
▶ Part 3: The Foreign Function Interface
▶ Part 4: Handling side effects
[Faculty of Science
Information and Computing
Sciences]
2
Part 1 -
▶ Where Purescript fits in the world
▶ A quick HOWTO
▶ Language introduction
[Faculty of Science
Information and Computing
Sciences]
3
Where Purescript fits in the world
▶ What is it?
The big text on the website (purescript.org):
PureScript is a small strongly typed programming language
that compiles to JavaScript.
It is a statically typed, compiled, functional language.
Someone with Haskell experience should feel at home.
[Faculty of Science
Information and Computing
Sciences]
4
Where would you use it?
▶ Where you want a functional environment for
Javascript
▶ Given its JavaScript target, the common use case is for
web development - both back and front end.
▶ It happily exists inside a command line environment
▶ Has a foreign function interface, so could be used
anywhere JavaScript is used.
▶ There also exist non-Javascript backends, to varying
degrees of completion, so other bindings are possible.
[Faculty of Science
Information and Computing
Sciences]
5
The language and Implementation
▶ A language and an implementation are different - in
theory.
▶ Right now, there is one implementation. Along with
this, there is a lot of common best practice.
▶ (or at least, practice).
[Faculty of Science
Information and Computing
Sciences]
6
Compiling
▶ The language compiles outside its runtime environment
- ala C++, Java.
▶ The output of the compiler is Javascript.
▶ From a purely mechanical standpoint, compilation is a
string -> string conversion.
▶ That is, there is no “magic” - no private hooks into
JavaScript or Web browsers.
▶ The compiler is implemented in Haskell, but this has no
practical effect when using it.
[Faculty of Science
Information and Computing
Sciences]
7
▶ Where it fits in the world
▶ A quick HOWTO
[Faculty of Science
Information and Computing
Sciences]
8
Standard Tools - pulp
pulp is the general front-end tool for Purescript
development.
▶ Generates a project environment - directory structure
and boilerplate
▶ Invokes the compiler as required (ala make)
▶ Launches your program on the cmdline
▶ Automates running test suites
[Faculty of Science
Information and Computing
Sciences]
9
Standard Tools - pulp - continued
▶ Has a built in http server for running a web app -
automates starting the front- and back-end
No self-respecting language these days comes without a
package repository - eg CTAN / CPAN / CRAN / PyPI. Thus -
▶ Can upload your package to persuit - the Purescript
package archive.
▶ Don’t confuse it with at least one other pulp project
(package management)
[Faculty of Science
Information and Computing
Sciences]
10
Standard Tools - psc, bower
▶ psc is the purescript compiler. In general, one will use
pulp to invoke this.
▶ bower is a standard package manager from the
javascript world.
▶ Module dependencies are brought into the project
using bower (bower.json)
[Faculty of Science
Information and Computing
Sciences]
11
Modules
▶ Module handling not as transparent as it first appears -
unlike R, Python et al
▶ Modules must be both imported into source code, and
into the project (bower.json)
[Faculty of Science
Information and Computing
Sciences]
12
Hello, world!
Firstly, create a project environment with pulp -
$ pulp init
[verbose output removed]
Now, contained in src/Main.purs:
module Main where
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)
main :: forall e. Eff (console :: CONSOLE | e) Unit
main = do
log "Hello sailor!" [Faculty of Science
Information and Computing
Sciences]
13
Hello, world! - Running on the commandline
$ pulp run
* Building project in/Users/nick/ps-test3
Compiling Data.Show
Compiling Data.Boolean
Compiling Control.Semigroupoid
[output trimmed]
* Build successful.
Hello sailor!
[Faculty of Science
Information and Computing
Sciences]
14
Hello, world! - compiled as JavaScript
output/Main/index.js:
// Generated by psc version 0.10.3
"use strict";
var Prelude = require("../Prelude");
var Control_Monad_Eff = require("../Control.Monad.Eff"
var Control_Monad_Eff_Console = require("../Control.Mo
var main = Control_Monad_Eff_Console.log("Hello sailor
module.exports = {
main: main
};
[Faculty of Science
Information and Computing
Sciences]
15
Hello, world! - Running in a browser
Starting the backend -
$ pulp server
* Server listening on http://localhost:1337/
* Building project in /Users/nick/ps-test2
* Build successful.
* Bundling JavaScript...
* Bundled.
[Faculty of Science
Information and Computing
Sciences]
16
Hello, world! - Running in a browser
Figure 1: Where did our text go?
[Faculty of Science
Information and Computing
Sciences]
17
Hello, world! - Running in a browser
Where did our text go?
▶ The problem is that we printed to “stdout”;
▶ As this is a gui environment we’re not going to see
anything until we create some gui objects.
[Faculty of Science
Information and Computing
Sciences]
18
Hello, world! - Running in a browser
Figure 2: There it is
[Faculty of Science
Information and Computing
Sciences]
19
Interactive console mode
Fire up an interactive session ala python, ghci (with
limitations!)
$ pulp psci
PSCi, version 0.10.3
Type :? for help
> import Prelude
> import Control.Monad.Eff.Console
> log "hello, nautical professional!"
hello, nautical professional!
unit
> 7+5
12
[Faculty of Science
Information and Computing
Sciences]
20
▶ Where it fits in the world
▶ A quick HOWTO
▶ Language introduction
[Faculty of Science
Information and Computing
Sciences]
21
Language Overview - Basic Types
▶ JavaScript Basic types: Number, String, Boolean
▶ Additional Native types: integers, characters, arrays,
records, and functions
[Faculty of Science
Information and Computing
Sciences]
22
Basic Types - Numbers
> :type 1
Int
> :type 1.7
Number
[Faculty of Science
Information and Computing
Sciences]
23
Basic Types - Boolean
> :type true
Boolean
> :type false
Boolean
[Faculty of Science
Information and Computing
Sciences]
24
Basic Types - Strings
> :type "boat"
String
:type 'b'
Char
[Faculty of Science
Information and Computing
Sciences]
25
Basic Types - Strictness
▶ :: gives a type signature
> true :: Boolean
true
[Faculty of Science
Information and Computing
Sciences]
26
Basic Types - Strictness
▶ Predictably -
> 7 :: Boolean
Error found:
in module $PSCI
at line 1, column 1 - line 1, column 3
Could not match type
Int
with type
Boolean
while checking that type Int is at least as general as
while checking that expression 7has type Boolean in va
[Faculty of Science
Information and Computing
Sciences]
27
Basic Types - Strictness
> true :: Int
Error found:
in module $PSCI
at line 1, column 1 - line 1, column 5
Could not match type
Boolean
with type
Int
while checking that type Boolean is at least as genera
while checking that expression true has type Int in va
[Faculty of Science
Information and Computing
Sciences]
28
Language Overview - Arrays
> [1,1,2,3,5,8] :: Array Int
[1,1,2,3,5,8]
> [1,1,2,3,5,8]
[1,1,2,3,5,8]
> :type [1,1,2,3,5,8]
Array Int
[Faculty of Science
Information and Computing
Sciences]
29
Language Overview - Arrays
Arrays must be homogeneous
> [1, 2, 2.5]
Error found:
in module $PSCI
at line 1, column 1 - line 1, column 11
Could not match type
Int
with type
Number [Faculty of Science
Information and Computing
Sciences]
30
Language Overview - Function
module Main where
import Prelude
import Control.Monad.Eff.Console (log, logShow)
fibonacci :: Int -> Int
fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n - 2) + fibonacci (n - 1)
main = do
logShow (fibonacci 7)
logShow (fibonacci 15)
[Faculty of Science
Information and Computing
Sciences]
31
Function - compiled
var fibonacci = function (v) {
if (v === 0) {
return 0;
};
if (v === 1) {
return 1;
};
return fibonacci(v - 2) + fibonacci(v - 1) | 0;
};
var main = function __do() {
Control_Monad_Eff_Console.logShow
(Data_Show.showInt)(fibonacci(7))();
return Control_Monad_Eff_Console.logShow
(Data_Show.showInt)(fibonacci(15))();
}; [Faculty of Science
Information and Computing
Sciences]
32
Function - running
$ pulp run
* Building project in/Users/nick/ps/interactive
Compiling Main
[much output removed]
* Build successful.
13
610
[Faculty of Science
Information and Computing
Sciences]
33
Features
▶ It has no runtime overhead
▶ Type security using javascript features
▶ Type classes
▶ Extensible records
▶ Human readable and debuggable output
▶ Many more such as: ADT’s, pattern matching, type
inference, rank-N types, etc.
[Faculty of Science
Information and Computing
Sciences]
34
Some examples
▶ It has no runtime
▶ Type security using javascript features
▶ Type classes
▶ Extensible records
▶ Human readable and debuggable output
▶ Many more such as: ADT’s, pattern matching, type
inference, rank-N types, etc.
I am going to talk about the bold faced topics.
[Faculty of Science
Information and Computing
Sciences]
35
Type security
Javascript actually only has one type, but they can be
roughly classified
value type
"" string
[] object
{} object
null object
undefined undefined
1 number
true boolean
(function(){}) function
Purescript has taken some measures to make sure that the
[Faculty of Science
generated code behaves well. Information and Computing
Sciences]
36
Type security
So this does not happen
> [] + {}
'[object Object]'
> {} + []
0
[Faculty of Science
Information and Computing
Sciences]
37
Type security
So this does not happen
> [] + {}
'[object Object]'
> {} + []
0
WAT
[Faculty of Science
Information and Computing
Sciences]
37
Type security
for example, this definition
x :: Int
x = 5
will be
var x = 5 | 0;
in compiled purescript. Why?
[Faculty of Science
Information and Computing
Sciences]
38
Type classes
They are very similar to Haskells, except that dependent
type classes are defined with
class (Eq a) <= Ord a where
...
Note that the arrow is inverted. This was a subtle design
choice for the purescript developers.
[Faculty of Science
Information and Computing
Sciences]
39
Type classes
When defining instances they have to be named
instance showVector :: Show Vector where
show = showV
showV :: forall a. Show a => Vector a -> String
showV (Vector {x: x, y: y}) =
"[" <> show x <> "; " <> show y <> "]"
Things to note: Explicit forall, concatenation operator,
pattern matching on records,
[Faculty of Science
Information and Computing
Sciences]
40
Extensible records
Sticking to the records from before, and we have a data type
data Vector = Vector { x :: Double, y :: Double}
then, in purescript syntax
type Vector = { x :: Number, y :: Number } -- Not even
will introduce a Vector constructor in PureScript that
accepts an object type. It is filled in for you.
[Faculty of Science
Information and Computing
Sciences]
41
Extensible records
So we can do this
origin :: Vector
origin = {x: 0, y: 0 } -- JSON syntax setters
or this
originX :: Number
originX = origin.x -- Object field getters
If you pattern match on a constructor then you have to do it
with JSON Syntax
[Faculty of Science
Information and Computing
Sciences]
42
Extensible records
Update syntax is similar to Haskells
setX :: Number -> Vector -> Vector
setX val point = point { x = val }
-- the same as
setX val (Vector {x: x, y: y}) = Vector {x: val, y: y}
-- if Vector would have a constructor
[Faculty of Science
Information and Computing
Sciences]
43
Differences with Haskell
Purescript is heavily influenced by Haskell, so what are the
differences?
[Faculty of Science
Information and Computing
Sciences]
44
Differences with Haskell
Purescript is heavily influenced by Haskell, so what are the
differences?
▶ Inverted type class arrow
▶ Explicit effects in the Eff Monad
main :: forall e . Eff (fs :: FS, trace :: Trace,
process :: Process | e) Unit
▶ Explicit forall
▶ No syntactic sugar for lists
▶ Type class instances are named
[Faculty of Science
Information and Computing
Sciences]
44
What does that compile to?
Let’s look at a few examples:
▶ Currying
▶ Algebraic data types
▶ Type class instance
[Faculty of Science
Information and Computing
Sciences]
45
Currying
Remember the input: add x y = x + y
add = function (x) {
return function(y) {
return (x + y | 0);
}
}
Not a lot of trickery going on here.
[Faculty of Science
Information and Computing
Sciences]
46
Algebraic data types
Let’s look at ADT’s. First we look at the Nothing constructor,
remember the input:
data Maybe a = Nothing | Just a
Nothing will then be
var Nothing = (function () {
function Nothing() {
};
Nothing.value = new Nothing();
return Nothing;
})();
[Faculty of Science
Information and Computing
Sciences]
47
Algebraic data types
Let’s look at ADT’s. First we look at the Nothing constructor,
remember the input:
data Maybe a = Nothing | Just a
Nothing will then be
var Nothing = (function () {
function Nothing() {
};
Nothing.value = new Nothing();
return Nothing;
})();
What is going on here? [Faculty of Science
Information and Computing
Sciences]
47
Algebraic data types
▶ As it turns out, this is a lexical closure.
▶ Protects the function body from global variable
definition ‘pollution’.
[Faculty of Science
Information and Computing
Sciences]
48
Algebraic data types
input data Maybe a = Nothing | Just a
Let’s see if we can dissect this function with the knowledge
from the previous slide.
var Just = (function () {
function Just(value0) {
this.value0 = value0;
};
Just.create = function(value0) {
return new Just(value0);
};
return Just;
})();
[Faculty of Science
Information and Computing
Sciences]
49
Type class instance
Assume that we want to have a nice Vector representation
in this way
psci> Vector 2 3
[2; 3]
data Vector = Vector { x :: Number, y :: Number }
instance showVector :: Show Vector where
show vec = "[" <> show vec.x <> "; " show vec.y <> "
[Faculty of Science
Information and Computing
Sciences]
50
Type class instance
var showVector = new Data_Show.Show(function (v) {
return "["
+ (Data_Show.show(Data_Show.showNumber)(v.x)
+ ("; "
+ (Data_Show.show(Data_Show.showNumber)(v.y)
+ "]")));
// String is constructed from right to left
It is immediately clear why they have chosen for named
instances.
[Faculty of Science
Information and Computing
Sciences]
51
The Foreign Function Interface
▶ Built to interface with JavaScript
▶ Native JavaScript support
▶ Compiles to user-friendly JavaScript
[Faculty of Science
Information and Computing
Sciences]
52
Calling PureScript from JavaScript
mul :: Int -> Int -> Int
mul x y = x * y
Compiles to:
var mul = function (x) {
return function (y) {
return x * y | 0;
};
};
Result:
> mul(3)(2);
6 [Faculty of Science
Information and Computing
Sciences]
53
Calling JavaScript from PureScript
To use foreign functions in a PureScript module we need to:
▶ add a JavaScript module with the same name as the
module
▶ expose the foreign function via that module
▶ declare the type of the JavaScript function object
[Faculty of Science
Information and Computing
Sciences]
54
Example: Math bindings
▶ Math.purs, the PureScript module in which the types
are declared
▶ Math.js, the JavaScript module in which the functions
are defined
The compiler will combine these into one module.
[Faculty of Science
Information and Computing
Sciences]
55
Math.purs
The PureScript module file contains the type declaration:
foreign import exp :: Number -> Number
[Faculty of Science
Information and Computing
Sciences]
56
Math.js
The JavaScript file belongs to the PureScript module, and
contains the function definition:
exports.exp = function (x) {
return Math.exp(x);
};
[Faculty of Science
Information and Computing
Sciences]
57
Calling the function:
> import Math (exp)
> exp 1
2.718
[Faculty of Science
Information and Computing
Sciences]
58
Type safety
Lets change our function:
exports.exp = function (x) {
return "Hello World!";
};
What happens now?
[Faculty of Science
Information and Computing
Sciences]
59
Running the function:
> import Math (exp)
> exp 1
"Hello World!"
Foreign functions are not type safe!
The declared type is only a label.
[Faculty of Science
Information and Computing
Sciences]
60
Multiple arguments
foreign import pow :: Number -> Number -> Number
exports.pow = function(x, y){
return Math.pow(x, y);
}
What’s wrong here?
[Faculty of Science
Information and Computing
Sciences]
61
Javascript:
> pow(3)
NaN
pow is not curried!
[Faculty of Science
Information and Computing
Sciences]
62
Type Safety
JavaScript functions are not type safe
PureScript provides the Foreign module
[Faculty of Science
Information and Computing
Sciences]
63
The module gives us:
▶ Foreign: a type that can be any valid JavaScript value
▶ An F monad, which is a special case of the Except
monad.
▶ functions
▶ readInt :: Foreign -> F Int,
▶ readNumber :: Foreign -> F Number,
▶ readProp :: String -> Foreign -> F Foreign
▶ etc.
[Faculty of Science
Information and Computing
Sciences]
64
type F a = Except (NonEmptyList ForeignError) a
Analogous to Haskell’s Except monad
runExcept: Except e a -> Either e a
[Faculty of Science
Information and Computing
Sciences]
65
Provided by PureScript:
readInt :: Foreign -> F Int
Our new halve import:
foreign import halve :: Int -> Foreign
[Faculty of Science
Information and Computing
Sciences]
66
> runExcept $ (readInt <<< halve) 4
(Right 2)
> runExcept $ (readInt <<< halve) 3
(Left (NonEmptyList (NonEmpty
(TypeMismatch "Int" "Number") Nil)))
[Faculty of Science
Information and Computing
Sciences]
67
Currying and uncurrying
PureScript offers:
▶ Fn0, Fn1, Fn2 … Fn10: Datatypes representing an
uncurried function with N arguments.
▶ runFn1, runFn2 … : Curry functions.
▶ mkFn1, mkFn2 … : Uncurry functions.
[Faculty of Science
Information and Computing
Sciences]
68
import Data.Function.Uncurried (Fn2, runFn2)
foreign import pow :: Fn2 Number Number Number
pow' -> Number Number Number
pow' = runFn2 pow
runFn2 curries the function:
runFn2 :: Fn2 a b c -> a -> b -> c
[Faculty of Science
Information and Computing
Sciences]
69
import Data.Function.Uncurried (Fn2, mkFn2)
add :: Fn Number Number Number
add = mkFn2 add'
where add' x y = x + y
mkFn2 uncurries the function:
mkFn2 :: (a -> b -> c) -> Fn2 a b c
[Faculty of Science
Information and Computing
Sciences]
70
What about side effects?
Functions with side effects need to be declared as Eff
monads, with an appropriate effect type.
For example, Console.log is declared as:
foreign import log
:: forall eff
. String
-> Eff (console :: CONSOLE | eff) Unit
[Faculty of Science
Information and Computing
Sciences]
71
part 4: Handling side effects
Figure 3: Obligatory XKCD
[Faculty of Science
Information and Computing
Sciences]
72
Eff monad
▶ Side effects are handled in with the Eff monad.
▶ More granular than Haskell’s IO monad.
[Faculty of Science
Information and Computing
Sciences]
73
side effects in Haskell
main :: IO ()
Meaning: main has side-effects.
[Faculty of Science
Information and Computing
Sciences]
74
side effects in PureScript
main :: Eff (fs :: FS,
trace :: Trace,
process :: Process) Unit
Meaning: The main function…
▶ uses the file system
▶ can trace out to the console
▶ does something to the current process
[Faculty of Science
Information and Computing
Sciences]
75
granularity makes sense
JavaScript was designed for interactivity in the browser.
Typical PureScript program has more I/O than typical Haskell
program.
[Faculty of Science
Information and Computing
Sciences]
76
there are a lot of effects
Examples of general purpose effects:
▶ Console IO
▶ Random number generation
▶ Exceptions
▶ Reading/writing mutable state
▶ …
Examples of browser effects:
▶ DOM manipulation
▶ AJAX calls
▶ talking to a websocket
▶ reading/writing in local storage
▶ …
[Faculty of Science
Information and Computing
Sciences]
77
example: printing a random number
module Main where
import Prelude
import Control.Monad.Eff
import Control.Monad.Eff.Random
import Control.Monad.Eff.Console
main :: Eff (console :: CONSOLE, random :: RANDOM) Unit
main = do
n <- random
logShow n
▶ returned 0.5547845012090082 for me
[Faculty of Science
Information and Computing
Sciences]
78
Eff is magic (and fast!)
The compiler knows about the Eff monad.
Optimizes away calls to >>= (bind)
I/O is as fast as JavaScript
[Faculty of Science
Information and Computing
Sciences]
79
var main =
Prelude[">>="]
(Control_Monad_Eff.monadEff())
(Control_Monad_Eff_Random.random)
(function (n) {
return Control_Monad_Eff_Console.logShow(
Prelude.showNumber())(n);
});
[Faculty of Science
Information and Computing
Sciences]
80
var main = function __do() {
var n = Control_Monad_Eff_Random.random();
return Control_Monad_Eff_Console.logShow(
Prelude.showNumber())(n)();
};
[Faculty of Science
Information and Computing
Sciences]
81
Define your own effect
You can import JavaScript code as an effect.
Let’s implement a counter!
[Faculty of Science
Information and Computing
Sciences]
82
Example: counter
PureScript code:
foreign import data COUNTER :: !
foreign import incrCounter :: Eff (
counter :: COUNTER) Number
[Faculty of Science
Information and Computing
Sciences]
83
JavaScript code:
exports.incrCounter = function() {
return ++globalCounter;
};
[Faculty of Science
Information and Computing
Sciences]
84
main :: Eff (console :: CONSOLE,
counter :: COUNTER) Unit
main = do
a <- incrCounter
b <- incrCounter
c <- incrCounter
logShow a
logShow b
logShow c
▶ returns 1 2 3
[Faculty of Science
Information and Computing
Sciences]
85
Conclusion
Great alternative to JavaScript.
In relation to Haskell:
▶ generally more precise
▶ generally more verbose
▶ generally more orthogonal
[Faculty of Science
Information and Computing
Sciences]
86
External Links
▶ Language Website
http://www.purescript.org
▶ Getting started
http://www.purescript.org/learn/getting-started
▶ Purescript by Example
https://leanpub.com/purescript/read
▶ Try Purescript
http://try.purescript.org
▶ Try Thermite
http://try.purescript.org/?backend=thermite
▶ Browserify
http://browserify.org
[Faculty of Science
Information and Computing
Sciences]
87