Download as pdf or txt
Download as pdf or txt
You are on page 1of 90

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

You might also like