Introduction to FP

(a C++ perspective).

Let's replace FP with just two restrictions:

Avoid throwing exceptions.

Avoid mutations.

How do we phase out or limit exceptions, global state, and loops?

A convincing demonstration of correctness being impossible as long as the mechanism is regarded as a black box, our only hope lies in not regarding the mechanism as a black box.

Why would we do this?

The good compiler  is a actually a proof assistant; so you can leverage types as propositions.

... Proof Assistant? ...

Types ≃ Propositions ≃ Objects

Terms ≃ Proofs ≃ Morphisms

Curry-Howard(-Lambek) Correspondance.

//In other words:
struct True; // not true: Boolean

We can prove logical statements by constructing elements of types

https://github.com/DanielKrawisz/Truth

Types ≃ Propositions

If you can define the method, you found the proof...

template <typename A, typename B> 
struct And { 
  A fst; 
  B snd; 
};

template <typename A, typename B> 
union Or { 
  A fst; 
  B snd; 
};

template <typename A, typename B>
B Implies(in: A);

Types ≃ Propositions

Updated for C++ 17

template<typename A, typename B>
using And = std::tuple<A, B>;

template<typename A, typename B>
using Or = std::variant<A, B>;

template<typename A, typename B>
using Implies = std::function<B(A)>;

What are we looking to write more of:

  • Total Functions
  • Referential Transparency
  • Proovable Termination

Why: Determinism

Total Functions

Avoid exceptions

int decode(std::string& s);

tl::expected<int, internal_error> 
decode(std::string& s);

Total Functions

Restrict input values

void open(std::string& filename, std::string& mode);

tl::expected<fstream, io_exception> 
open(std::string& filename, mode_enum mode);

Referential Transparency

Avoid mutation

int x = 5;
x = 3;
const int y = 5;
y = 3; // Exception

if (x = 1) // Oops

std::fstream fs;
fs.open("w")
fs.open("y") // Oops

Referential Transparency

Decouple evaluation

IO<Tiemstamp> systemTime;

systemTime()
doSomething()
sleep(1)
systemTime()
doSomethingElse()

Termination

Avoid recursion

int use(int a) { return use(a); };

int sum(int... nums) = { return (... + nums); };

Termination

Avoid loops

int use(int a) { while (true) { ... }; return a; };

mlib::lazylist<int> fib = mlib::lazylist::single<int>(0)
  .scanLeft(1)([] (int a, int b) { return a + b; });

This subset of your code composes!

Category Theory is the study of composition.

The code your write this ways obeys the laws of category theory.

Introduction to FP

By Rodolfo Hansen

Introduction to FP

Let the compiler prove itself to you

  • 100