7.1.2 Equational Reasoning

Equational reasoning is possible with pure functions, because the equality is not hiding state mutation. Thus, reasoning about code in functional programming language like Haskell becomes more explicitly mathematical.

7.1.3 Optional

Skipping C++ content. Perhaps I will come back and do this with Rust

7.1.4 Typeclasses

Suppose Maybe is a Functor. Maybe is a type constructor. This means Maybe itself is the mapping from objects to objects (* -> *) e.g. Int -> Maybe Int.

But to be a functor, Maybe needs a mapping from morphisms to morphisms. This is fmap, which is declared in Haskell in the Functor class:

7.3 Functor Composition

to this list. The specific implementation of isPrime isn’t important, it just returns True if the Int is prime and False if it is not prime.

But we have a problem here, in that prime numbers are only defined for integers greater than 1. isPrime 3 should return True and isPrime 4 should return False, but isPrime 1 is undefined and should return neither of those things.

Now, you might be saying, why don’t we just return False for any integer where isPrime is undefined? This does seem like the simplest solution from a programming perspective.

However, this solution could actually generate a lot of headache for us later on.

For example, let’s say we wanted to write another function isComposite, that returns True for integers with factors.

But if we defined isPrime 1 to be False then isComposite 1 would return True, which is wrong, because “composite” numbers are also only defined for numbers greater than 1.

Now, suppose we have some code (maybe from a library) where isPrime and isComposite do follow the rule that each is the logical inverse (the “not) of the other.

But let’s say that for whatever reason, the author decided to make isPrime and isComposite partial functions, meaning that for any integers outside their domain (less than or equal to 1) they return a runtime error.