PureScript Cheatsheet

Another rescue for JavaScript

#Functions

Basic function

double a = a * 2
A function, returning the doubled input parameter. Everything behind the equals-sign is the return.

Providing types

double :: Int -> Int 
double a = a * 2
We are not forced to, but it's a best practise to provide the types of parameters and the return.

More than one parameter

add :: Int -> Int -> Int 
add a b = a + b
Since functions are curried, just add another Int -> for each parameter. More on currying

Recursive functions

sumToTen :: Int -> Int 
sumToTen n = 
  if n == 10 then 
    10 
  else 
    sumToTen (n + 1)

main = log (show (sumToTen 0))
"10"
This recursive function calls the function as long the number is < 10. Of course, this is absolutely useless, because it makes out of any passed number below 10 a 10 as return.

do-keyword in functions

factors :: Int -> Array (Array Int)
factors n = filter (\xs -> product xs == n) do
  i <- 1 .. n
  j <- i .. n
  [ [ i, j ] ]
The do keyword allows to make code in functions more readable and to work with different expressions.
In the example, "j" and "i" are both expressions, used in a final expression at the bottom which is finally returned.

Impure functions

logSomething :: String -> Effect Unit
logSomething msg = log ("Message: " <> msg)

main = logSomething "Hello"
By definition, pure functions do not change anything outside of their scope. Logging something in the console therefore is an impure function. Of course we can log something from a function, instead of returning a value.

#Using the console

Logging in the console

import Effect (Effect)

main = log "Hello world"
To log something in the console, make sure to import the proper package.

Logging more than one thing

main = do
  log "Hello world"
  log "Hello World"
The do-keyword helps us to do so.

Logging a variable

name = "John Doe"

main = log (show name)

Logging a function call

double :: Int -> Int 
double x = x * 2

main = log (show (double 2))
Please notice the braces.

Concatenating in the console

doubleNumber :: Int -> Int
doubleNumber x = x * 2

printDoubleNumber :: Int -> String 
printDoubleNumber x = "The Result: " <> (show (doubleNumber x))

main = log (printDoubleNumber 2)
"The Result: 4" 
We have a function for doubling an integer. The second function calls the first one, but converts and returns the first ones value to a string. Plus, it concatinates it with another string.

#Records

What are records?

Records are what we would call objects in JavaScript and some other languages. Just like in JS, we can access properties with the (.) operator.
Compared to JS, records are immutable.

Basic record with blueprint

type Person =
{ name :: String,
  age :: Int }

max :: Person
max = { name: "Max", age: 22 }

main = log (show max)
-- { age: 22, name: "Max" }

Accessing record properties

main = log (show max.name)
-- "Max"
-- using a function to access it: 
getName :: Person -> String 
getName person = person.name 

main = log (getName max)

#Conditionals

If-else in PureScript

biggerThan10 :: Int -> String
biggerThan10 num =
  if num > 10
  then "Number is > 10"
  else "Number is NOT > 10"

main = log (biggerThan10(2))
"Number is NOT > 10"

Passing a condition to a function

test :: Boolean -> String
test condition =
  if condition
  then "true"
  else "false"

main = log (test(1 > 2))
"false"

#Map & Filter

Map

Map transforms a structure based on a pattern, which can be applied to each element. The transformed structure is returned, with mutating the original structure.

Mapping an array

map (\n -> n * 2) [1, 2, 3]
[2, 4, 6]
Maps over the array, doubles each value in it and returns as a new array

Passing a function into map

addOne :: Int -> Int
addOne x = x + 1

newArr = map addOne [1, 2, 3]
-- for a predefined function
map show [1, 2, 3]
-- ["1","2","3"]

Filter

A filter can be applied to a structure, to only copy the values matching a certain pattern.
Just as map, filter does not mutate anything.

Filter on array

filter (\n -> mod n 2 == 0) [1, 2, 3, 4, 5, 6]
[2, 4, 6]

#Arrays

Generating an array in range

range 0 5
-- [0,1,2,3,4,5]
-- or:
(0 .. 5)

Concatenating arrays

concat [[1, 2, 3], [4, 5]]
-- [1,2,3,4,5]
The concat function takes two arrays within one array and concats the inner arrays.

The concat map

map (\n -> [n, n * n]) (1 .. 5)
-- [[1,1],[2,4],[3,9],[4,16],[5,25]]
-- A two-dimensional array? Let's fix this:
concatMap (\n -> [n, n * n]) (1 .. 5)
-- [1,1,2,4,3,9,4,16,5,25]
-- Much better!
Map receives a function from values to values.
Concat Map takes a function from values to arrays of values.

#Folds

What are folds?

Folds feel a little bit like reduce. They help to turn a data structure into a single value. We can choose from which direction on to traverse.

What to import:

import Data.Foldable

foldr vs. foldl

foldr stands for "fold from the right", and foldl which stands for "fold from the left".

Using folds

foldl (+) 0 [1, 2, 3]
-- 6

foldr (+) 0 [1, 2, 3]
-- 6
No matter the direction, both fold-functions sum up the values of the array. We can provide other operations than just addition (next snippet).

Fold accumulator

foldl (*) 0 [1, 2, 3, 4]
-- 0

foldl (*) 1 [1, 2, 3, 4]
-- 24
Why does multiplying all the values with each other lead to "0" in the first example? Because of the "0" we provide after the operator. It can be thought of as an accumulator, accumulating a result after we traversed the array.

#Miscellaneous

Pattern matching

multiply :: Int -> Int -> Int 
multiply a 0 = 0 
multiply 0 a = 0 
multiply a b = a + b 
As you can see, this looks a bit like function-overloading. Pattern matching allows us to structure our code into cases of arguments.

Pattern matching with other types

toString :: Boolean -> String
toString true  = "true"
toString false = "false"

Array pattern matching

isEmpty :: forall a. Array a -> Boolean
isEmpty [] = true
isEmpty _ = false

Guards

biggerNumber :: Int -> Int -> Int 
biggerNumber a b | a > b = a 
                 | otherwise = b 
Guards are an alternative to the if-else syntax. The function returns the bigger of both passed numbers.

Switch Case

enterNumber:: Int -> String  
enterNumber num = case num of 
  0 -> "You entered 0"
  1 -> "You entered 1"
  2 -> "You entered 2"
  _ -> "You entered a different number"
Syntax: case of. The _ covers all other cases.