# Awesome

# Functional Programming Jargon in Julia

Functional programming (FP) provides many advantages, and its popularity has been increasing as a result. However, each programming paradigm comes with its own unique jargon and FP is no exception. By providing a glossary, we hope to make learning FP easier.

This is a fork of Functional Programming Jargon. Examples are presented in Julia.

This document is WIP and pull requests are welcome!

**Translations**

**Table of Contents**

- Arity
- Higher-Order Functions (HOF)
- Closure
- Partial Application
- Currying
- Auto Currying
- Function Composition
- Continuation
- Purity
- Side effects
- Idempotent
- Point-Free Style
- Predicate
- Contracts
- Category
- Value
- Constant
- Functor
- Pointed Functor
- Lift
- Referential Transparency
- Equational Reasoning
- Lambda
- Lambda Calculus
- Lazy evaluation
- Monoid
- Monad
- Comonad
- Applicative Functor
- Morphism
- Setoid
- Semigroup
- Foldable
- Lens
- Type Signatures
- Algebraic data type
- Option
- Function

## Arity

The number of arguments a function takes. From words like unary, binary, ternary, etc. This word has the distinction of being composed of two suffixes, "-ary" and "-ity." Addition, for example, takes two arguments, and so it is defined as a binary function or a function with an arity of two. Such a function may sometimes be called "dyadic" by people who prefer Greek roots to Latin. Likewise, a function that takes a variable number of arguments is called "variadic," whereas a binary function must be given two and only two arguments, currying and partial application notwithstanding (see below).

```
julia> myadd(x,y) = x + y
julia> arity = first(methods(myadd)).nargs - 1
2
# The arity of `myadd` is 2
```

## Higher-Order Functions (HOF)

A function which takes a function as an argument and/or returns a function.

```
julia> filter(iseven, 1:5)
2-element Vector{Int64}:
2
4
julia> sum(abs, [-1, -2, 3])
6
```

## Closure

A closure is a way of accessing a variable outside its scope. Formally, a closure is a technique for implementing lexically scoped named binding. It is a way of storing a function with an environment.

A closure is a scope which captures local variables of a function for access even after the execution has moved out of the block in which it is defined. ie. they allow referencing a scope after the block in which the variables were declared has finished executing.

```
# this function returns an anonymous function that **closes over** `init`
julia> add_to(init) = x -> x + init
julia> add_to_five = add_to(5)
julia> add_to_five(3)
8
```

Anonymous function Vs Closure: A anonymous is essentially a function that is defined inline rather than the standard method of declaring functions. Lambdas can frequently be passed around as objects.

A closure is a function that encloses its surrounding state by referencing fields external to its body. The enclosed state remains across invocations of the closure.

**Further reading/Sources**

## Partial Application

Partially applying a function means creating a new function by pre-filling some of the arguments to the original function.

```
julia> add3(a, b, c) = a + b + c
add3 (generic function with 1 method)
julia> partial(f, args...) = (x...) -> f(args..., x...)
julia> five_plus = partial(add3, 2, 3)
julia> five_plus(4)
9
julia> one_plus_plus = partial(add3, 1)
#1 (generic function with 1 method)
julia> one_plus_plus(2,3)
6
#special case of 2-arguments original function
julia> six_plus = Base.Fix1(+, 6)
(::Base.Fix1{typeof(+), Int64}) (generic function with 1 method)
julia> six_plus(7)
13
```

Partial application helps create simpler functions from more complex ones by baking in data when you have it. Curried functions are automatically partially applied.

## Currying

The process of converting a function that takes multiple arguments into a function that takes them one at a time.

Each time the function is called it only accepts one argument and returns a function that takes one argument until all arguments are passed.

```
julia> curried_add(a) = b -> a + b
julia> curried_add(40)(2)
42
julia> add2 = curried_add(2)
julia> add2(10)
12
```

## Auto Currying

Transforming a function that takes multiple arguments into one that if given less than its correct number of arguments returns a function that takes the rest. When the function gets the correct number of arguments it is then evaluated.

The Curries.jl package has an currying decorator which works this way.

```
julia> using Currier
julia> @curried add3(x, y, z) = x + y + z
julia> add3(1,2,3)
6
julia> add3(1,2)(3)
6
julia> add3(1)(2)(3)
6
```

**Further reading**

## Function Composition

The act of putting two functions together to form a third function where the output of one function is the input of the other.

```
# `∘` can be typed with \circ<tab>
julia> floor_and_string = string ∘ floor
julia> floor_and_string(121.212121)
"121.0"
```

## Continuation

At any given point in a program, the part of the code that's yet to be executed is known as a continuation.

```
julia> print_as_string(num) = println("Given $num")
julia> function add_one_and_continue(num, cc)
result = num + 1
cc(result)
end
julia> add_one_and_continue(2, print_as_string)
Given 3
```

## Purity

A function is pure if the return value is only determined by its input values, and does not produce side effects. Be very careful when using @pure in Julia.

```
julia> greet(name) = "Hi, $name"
julia> greet("Brianne")
"Hi, Brianne"
```

As opposed to each of the following:

```
julia> name = "Brianne"
"Brianne"
julia> greet() = "Hi, $name"
greet (generic function with 2 methods)
julia> greet()
"Hi, Brianne"
```

The above example's output is based on data stored outside of the function...

```
julia> greeting = nothing
julia> function greet(name)
global greeting
greeting = "Hi, $name"
return nothing
end
julia> greet("Brianne")
julia> greeting
"Hi, Brianne"
```

... and this one modifies state outside of the function.

## Side effects

A function or expression is said to have a side effect if apart from returning a value, it interacts with (reads from or writes to) external mutable state.

```
println("IO is a side effect!")
```

## Idempotent

A function is idempotent if reapplying it to its result does not produce a different result.

```
identity(identity(10))
```

```
# this relys on sort algorithm being stable
sort(sort(sort([2, 1])))
```

## Point-Free Style

Writing functions where the definition does not explicitly identify the arguments used. This style usually requires currying or other Higher-Order functions. A.K.A Tacit programming.

```
# Given
julia> mymap = fn -> vec -> map(fn, vec)
julia> myadd = a -> b -> a + b
# Then
# Not points-free - `numbers` is an explicit argument
julia> increment_all = numbers -> mymap(myadd(1))(numbers)
julia> increment_all([4,7,8])
3-element Vector{Int64}:
5
8
9
# Points-free - The list is an implicit argument
julia> increment_all2 = mymap(myadd(1))
julia> increment_all([4,7,8])
3-element Vector{Int64}:
5
8
9
```

`increment_all`

identifies and uses the parameter `numbers`

, so it is not points-free. `increment_all2`

is written just by combining functions and values, making no mention of its arguments. It **is** points-free.

Points-free function definitions look just like normal assignments without `function ...`

or `blah -> `

.

## Predicate

A predicate is a function that returns true or false for a given value. A common use of a predicate is as the callback for array filter.

```
julia> filter(>(2), [1, 2, 3, 4])
2-element Vector{Int64}:
3
4
```

## Contracts

A contract specifies the obligations and guarantees of the behavior from a function or expression at runtime. This acts as a set of rules that are expected from the input and output of a function or expression, and errors are generally reported whenever a contract is violated.

```
julia> add1(num::Integer) = num + 1
julia> add1(num) = throw(ArgumentError("Contract violated: expected `num::Integer`"))
julia> add1(2)
3
julia> add1("blah")
ERROR: ArgumentError: Contract violated: expected `num::Integer`
```

## Category

A category in category theory is a collection of objects and morphisms between them. In programming, typically types act as the objects and functions as morphisms.

To be a valid category 3 rules must be met:

- There must be an identity morphism that maps an object to itself.
Where
`a`

is an object in some category, there must be a function from`a -> a`

. - Morphisms must compose.
Where
`a`

,`b`

, and`c`

are objects in some category, and`f`

is a morphism from`a -> b`

, and`g`

is a morphism from`b -> c`

;`g(f(x))`

must be equivalent to`(g • f)(x)`

. - Composition must be associative
`f • (g • h)`

is the same as`(f • g) • h`

Since these rules govern composition at very abstract level, category theory is great at uncovering new ways of composing things.

**Further reading**

## Value

Anything that can be assigned to a variable.

```
('John', 30)
(name = 'Person', age = 30)
5
a -> a^2
[1.0]
missing
```

## Constant

A variable that cannot be reassigned once defined.

```
const five = 5
```

Constants are referentially transparent. That is, they can be replaced with the values that they represent without affecting the result.

## Functor

An object that implements a `map`

function which, while running over each value in the object to produce a new object, adheres to two rules:

**Preserves identity**

```
object.map(x => x) ≍ object
```

**Composable**

```
object.map(compose(f, g)) ≍ object.map(g).map(f)
```

(`f`

, `g`

are arbitrary functions)

In Julia `map`

takes the first argument as the function:

```
julia> map(identity, [1,2,3])
3-element Vector{Int64}:
1
2
3
julia> map(identity, (1,2,3))
(1, 2, 3)
```

and

```
julia> f(x) = x+1
julia> g(x) = x*2
julia> map(f, map(g, [1,2,3]))
3-element Vector{Int64}:
3
5
7
julia> map(f∘g, [1,2,3])
3-element Vector{Int64}:
3
5
7
```

## Pointed Functor

An object with an `of`

function that puts *any* number of values into it. The following property
`of(f(x)) == of(x).map(f)`

must also hold for any pointed functor. Better explaination

```
# here we think of `Vector` and `Base.map` together as a functor:
julia> of(t::AbstractVector{T}, vals...) where T = T[vals...]
julia> of([], 3)
1-element Vector{Any}:
3
julia> of(Int[], 3)
1-element Vector{Int64}:
3
julia> of(Float64[], 3)
1-element Vector{Float64}:
3.0
```

## Lift

Lifting is when you take a value and put it into an object like a functor. If you lift a function into an Applicative Functor then you can make it work on values that are also in that functor.

Some implementations have a function called `lift`

, or `liftA2`

to make it easier to run functions on functors.

```
julia> liftA2(fn) = (a, b) -> fn.(a, b) # cheating, rely on broadcast to figure out how to apply `fn`
liftA2 (generic function with 1 method)
julia> mult(a, b) = a * b
mult (generic function with 1 method)
julia> lifted_mult = liftA2(mult) # this function now works on functors (arrays)
#5 (generic function with 1 method)
julia> lifted_mult([1, 2], [3]) # [3, 6]
2-element Vector{Int64}:
3
6
julia> lifted_mult([1, 2], 3)
2-element Vector{Int64}:
3
6
julia> lifted_mult([1, 2], [3,4])
2-element Vector{Int64}:
3
8
```

## Referential Transparency

An expression that can be replaced with its value without changing the behavior of the program is said to be referentially transparent.

```
greet() = "Hello World!"
```

Any invocation of `greet()`

can be replaced with `Hello World!`

hence greet is
referentially transparent. In Julia this is trivially true for any `immutable`

object.

## Equational Reasoning

When an application is composed of expressions and devoid of side effects, truths about the system can be derived from the parts.

## Lambda

An anonymous function that can be treated like a value.

```
julia> f(a) = a + 1
f (generic function with 1 method)
julia> a -> a + 1
#3 (generic function with 1 method)
```

Lambdas are often passed as arguments to Higher-Order functions.

```
julia> filter(x -> x^2 < 10, 1:5)
3-element Vector{Int64}:
1
2
3
```

You can assign a lambda to a variable.

```
julia> addone = a -> a + 1
#1 (generic function with 1 method)
```

## Lambda Calculus

A branch of mathematics that uses functions to create a universal model of computation.

## Lazy evaluation

Lazy evaluation is a call-by-need evaluation mechanism that delays the evaluation of an expression until its value is needed. In functional languages, this allows for structures like infinite lists, which would not normally be available in an imperative language where the sequencing of commands is significant.

```
julia> struct myrand end
julia> Base.iterate(::myrand, state=nothing) = rand()
julia> first(myrand())
0.1381937787185451
```

## Monoid

An object with a function that "combines" that object with another of the same type.

One simple monoid is the addition of numbers:

```
julia> 1 + 1
2
```

In this case number is the object and `+`

is the function.

An "identity" value must also exist that when combined with a value doesn't change it.

The additive identity value for addition is `0`

, can also be obtained by `zero()`

:

```
julia> 1 + zero(1)
1
```

It's also required that the grouping of operations will not affect the result (associativity):

```
julia> 1 + (2 + 3) == (1 + 2) + 3
true
```

List concatenation also forms a monoid:

```
julia> vcat([1, 2], [3, 4])
4-element Vector{Int64}:
1
2
3
4
```

The identity value is empty Int array `Int[]`

(can be obtained by `empty()`

)

```
julia> vcat([1, 2], empty([1,2]))
2-element Vector{Int64}:
1
2
```

If identity and compose functions are provided, functions themselves form a monoid:

```
# `foo` is a 1-argument function
foo∘identity ≍ identity∘foo ≍ foo
```

## Monad

A monad is an object with `of`

and `chain`

functions. `chain`

is like `map`

except it un-nests the resulting nested object.

```
julia> of(t::AbstractVector{T}, vals...) where T = T[vals...]
julia> chain(fn, v) = mapreduce(fn, vcat, v)
julia> chain(split, of([], "cat dot", "fish bird"))
4-element Vector{SubString{String}}:
"cat"
"dot"
"fish"
"bird"
# Contrast to map
julia> map(split, of([], "cat dot", "fish bird"))
2-element Vector{Vector{SubString{String}}}:
["cat", "dot"]
["fish", "bird"]
```

`of`

is also known as `return`

in other functional languages.
`chain`

is also known as `flatmap`

and `bind`

in other languages.

## Comonad

An object that has `extract`

and `extend`

functions. `Ref`

is a natural condidate:

```
julia> extract(x::Ref) = x[]
julia> extend(x::T, f) where T<:Ref = T(f(x[]))
julia> extract(Ref(1.0))
1.0
```

Extend runs a function on the comonad. The function should return the same type as the comonad.

```
julia> extend(Ref(1.0), x->3) # notice the type is preserved correctly
Base.RefValue{Float64}(3.0)
```

## Applicative Functor

An applicative functor is an object with an `ap`

function. `ap`

applies a function in the object to a value in another object of the same type.

```
julia> ap(fn, val) = fn(val)
ap (generic function with 1 method)
julia> ap(fns::Vector, vals) = map(ap, fns, vals)
ap (generic function with 2 methods)
# Example usage
julia> ap([a->a+1], [1])
1-element Vector{Int64}:
2
```

This is useful if you have two objects and you want to apply a binary function to their contents.

```
# Arrays that you want to combine
arg1 = [1, 3]
arg2 = [4, 5]
# combining function - must be curried for this to work
julia> add(x) = y -> x + y
julia> partially_applied_adds = map(add, arg1)
```

This gives you an array of functions that you can call `ap`

on to get the result:

```
julia> ap(partially_applied_adds, arg2)
2-element Vector{Int64}:
5
8
```

## Morphism

A transformation function.

### Endomorphism

A function where the input type is the same as the output.

```
# String -> String
julia> uppercase("Aasd")
"AASD"
# Number -> Number
julia> decrement(num::T)::T = num-1
```

### Isomorphism

A pair of transformations between 2 types of objects that is structural in nature and no data is lost.

For example, 2D coordinates could be stored as an array `[2,3]`

or object `{x: 2, y: 3}`

.

```
julia> coords_to_pair(coords) = (coords.x, coords.y)
julia> pair_to_coords(pair) = (x=pair[1], y=pair[2])
julia> pair_to_coords(coords_to_pair((x=1, y=2)))
(x = 1, y = 2)
```

### Homomorphism

A homomorphism is just a structure preserving map. Weaker than isomorphism in that it doesn't require `bijectivity`

.
In fact, a functor is just a homomorphism between categories as it preserves the original category's structure under the mapping.

Julia's `map`

satisfy this (one-to-one/injective) unless the function is not "pure":

```
map(uppercase, ["oreos"]) == [uppercase("oreos")]
```

### Catamorphism

A `reduce_right`

function that applies a function against an accumulator and each value of the array (from right-to-left) to reduce it to a single value.

```
julia> sum_by_reduce(vec) = foldr(+, vec)
julia> sum_by_reduce([1,2,3])
6
```

### Anamorphism

An `unfold`

function. An `unfold`

is the opposite of `fold`

(`reduce`

). It generates a list from a single value.

```
julia> function unfold(f, seed)
function go(f, seed, acc)
res = f(seed)
return isnothing(res) ? acc : go(f, res[2], push!(acc, res[1]))
end
return go(f, seed, [])
end
unfold (generic function with 1 method)
julia> countDown(n) = unfold(n->ifelse(n<=0, nothing, (n, n-1)), 3)
countDown (generic function with 1 method)
julia> countDown(3)
3-element Vector{Any}:
3
2
1
```

### Hylomorphism

The combination of anamorphism and catamorphism.

### Paramorphism

A function just like `reduce_right`

. However, there's a difference:

In paramorphism, your reducer's arguments are the current value, the reduction of all previous values, and the list of values that formed that reduction.

```
julia> function para(reducer, accumulator, elements)
isempty(elements) && return accumulator
head, tail... = elements
return reducer(head, tail, para(reducer, accumulator, tail))
end
julia> suffixes(lst) = para((x, xs, sxs) -> [[xs]; sxs], [], lst)
julia> suffixes([1,2,3,4,5])
5-element Vector{Any}:
[2, 3, 4, 5]
[3, 4, 5]
[4, 5]
[5]
Int64[]
```

The third parameter in the reducer (in the above example, `[[xs]; sxs]`

) is kind of like having a history of what got you to your current acc value.

### Apomorphism

it's the opposite of paramorphism, just as anamorphism is the opposite of catamorphism. Whereas with paramorphism, you combine with access to the accumulator and what has been accumulated, apomorphism lets you `unfold`

with the potential to return early.

## Setoid

An object that has an `equals`

function which can be used to compare other objects of the same type.

In `Julia`

, just use `isequal(obj1, obj2)`

## Semigroup

An object that has a `concat`

function that combines it with another object of the same type.

```
# column-major
julia> vcat([1], [2])
2-element Vector{Int64}:
1
2
```

## Foldable

An object that has a `reduce`

function that applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.

```
julia> sum_by_reduce(vec) = reduce(+, vec)
julia> sum_by_reduce([1,2,3])
6
```

## Lens

A lens is a structure (often an object or function) that pairs a getter and a non-mutating setter for some other data structure.

A popular implementation is Accessors.jl

```
julia> using Accessors
# this is immutable
julia> struct T
a
b
end
julia> obj = T("AA", "BB");
julia> obj.a = "aa"
ERROR: setfield!: immutable struct of type T cannot be changed
julia> lens = @optic _.a
(@optic _.a)
julia> lens(obj)
"AA"
julia> set(obj, lens, 2)
T(2, "BB")
julia> obj # the object was not mutated, instead an updated copy was created
T("AA", "BB")
julia> modify(lowercase, obj, lens)
T("aa", "BB")
```

## Type Signatures

Often functions in JavaScript will include comments that indicate the types of their arguments and return values.

There's quite a bit of variance across the community but they often follow the following patterns:

```
# add :: int -> int -> int
myadd1 = (x::Int -> y::Int -> x + y)::Int
# increment :: int -> int
increment(x::Int)::Int = x + 1
```

Due to JIT, it's better to NOT over-specificy types, because it only reduces the genrality of your function without any speed gain, also see Multiple Dispatch for more idiomatic way to write parametric methods in Julia.

**Further reading**

- Ramda's type signatures
- Mostly Adequate Guide
- What is Hindley-Milner? on Stack Overflow

## Algebraic data type

A composite type made from putting other types together. Two common classes of algebraic types are sum and product.

### Sum type

A Sum type is the combination of two types together into another one. It is called sum because the number of possible values in the result type is the sum of the input types.

In Julia, it's almost the same as Type Unions, albeit some implementation details and lack of pattern matching, but because Julia is a dynamic language (i.e. behavior always correct when you take a value out of a union typed container), the line is blurred.

```
julia> a = Union{Float64, Int}[1,2,3]
3-element Vector{Union{Float64, Int64}}:
1
2
3
julia> typeof.(a)
3-element Vector{DataType}:
Int64
Int64
Int64
julia> a = Union{Float64, Int}[1,2,3.0]
3-element Vector{Union{Float64, Int64}}:
1
2
3.0
julia> typeof.(a)
3-element Vector{DataType}:
Int64
Int64
Float64
```

Sum types are sometimes called union types, discriminated unions, or tagged unions.

See an implementation SumTypes.jl.

### Product type

A **product** type combines types together in a way you're probably more familiar with:

```
point(p) = (x=p[1], y=p[2])
```

It's called a product because the total possible values of the data structure is the product of the different values. Many languages have a tuple type which is the simplest formulation of a product type.

See also Set theory.

## Option

Option is a sum type with two cases often called `Some`

and `None`

.

Option is useful for composing functions that might not return a value.

```
help?> Some
search: Some something @something @showtime VersionNumber test_colorscheme DimensionMismatch sortperm
Some{T}
A wrapper type used in `Union{Some{T}, Nothing}` to distinguish between the absence of a value (`nothing`)
and the presence of a `nothing` value (i.e. `Some(nothing)`).
Use `something` to access the value wrapped by a Some object.
```

`Option`

is also known as `Maybe`

. `Some`

is sometimes called `Just`

. `None`

is sometimes called `Nothing`

(as in Julia).

## Function

A **function** `f :: A => B`

is an expression - often called arrow or lambda expression - with **exactly one (immutable)** parameter of type `A`

and **exactly one** return value of type `B`

. That value depends entirely on the argument, making functions context-independant, or referentially transparent. What is implied here is that a function must not produce any hidden side effects - a function is always pure, by definition. These properties make functions pleasant to work with: they are entirely deterministic and therefore predictable. Functions enable working with code as data, abstracting over behaviour:

```
julia> times2(n::Number) = (n * 2)::Number
times2 (generic function with 1 method)
julia> map(times2, [1, 2, 3])
3-element Vector{Int64}:
2
4
6
```