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 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 ++, 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 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 , 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.Monad.Eff.Console"); 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 type Boolean while checking that expression 7has type Boolean in value declaration it [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 general as type Int while checking that expression true has type Int in value declaration it

[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, , , 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 WAT

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 a constructor

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 ▶ Inverted 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

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 What is going on here?

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 ▶ 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