Joseph Haugh
University of New Mexico
Q: What is a type class?
A: A collection of related types.
Q: What does the following list comprehension do?
[ [x, y] | x <- [0..2], y <- [0..2], x /= y ]
A:
[[0,1],[0,2],[1,0],[1,2],[2,0],[2,1]]
Last class I posed the question: Can we generalize the pattern
of defining (*)
in terms of (+)
, (^)
in terms of (*)
, and
so on?
Yes!
First let me remind you of the definition of (***)
and (^^^)
:
(***) :: Int -> Int -> Int
m *** 0 = 0
m *** n = m + (m *** (n - 1))
(^^^) :: Int -> Int -> Int
m ^^^ 0 = 1
m ^^^ n = m *** (m ^^^ (n - 1))
Can you spot the pattern?
Try to generalize this pattern by first defining a function
hyper
which takes three arguments:
x
the level of recursion (level 1 is addition, level 2 is multiplication and so on)m
is the “base”n
is the “exponent”hyper 2 2 3 = 6 = 2 * 3 = 2 + 2 + 2
hyper 3 2 3 = 8 = 2 ^ 3 = 2 * 2 * 2
hyper 4 2 3 = 16 = 2 ^!^ 3 = 2 ^ (2 ^ 2)
hyper 5 2 3 = 65536 = 2 ^!!^ 3 = 2 ^!^ (2 ^!^ 2)
hyper 6 2 3 = BIG# = 2 ^!!!^ 3 = 2 ^!!^ (2 ^!!^ 2)
Step 1: Define the type
hyper :: Int -> Integer -> Integer -> Integer
Hold up! What is the difference between Int
and Integer
?
Int
is a fixed-precision integer type, while Integer
is an arbitrary-precision integer type.
Step 2: Enumerate the cases
Ask your self what are we recursing on?
hyper 1 m n = ...
hyper 2 m n = ...
hyper 3 m n = ...
hyper x m 0 = ...
hyper x m 1 = ...
hyper x m n = ...
Why so many base cases?
Step 3: Define the simple cases
hyper 1 m n = m + n
hyper 2 m n = m * n
hyper 3 m n = m ^ n
hyper x m 0 = 1
hyper x m 1 = m
hyper x m n = ...
Step 4: Define the other cases
hyper 1 m n = m + n
hyper 2 m n = m * n
hyper 3 m n = m ^ n
hyper x m 0 = 1
hyper x m 1 = m
hyper x m n = hyper (x - 1) m (hyper x m (n - 1))
Step 4: Define the other cases
hyper 1 m n = m + n
hyper 2 m n = m * n
hyper 3 m n = m ^ n
hyper x m 0 = 1
hyper x m 1 = m
hyper x m n = hyper (x - 1) m (hyper x m (n - 1))
m `hyper (x - 1)` (m `hyper x` (n - 1))
m *** (m ^^^ (n - 1))
Step 5: Generalize and simplify
You could generalize the type to use Num
, Integral
and Eq
type classes.
hyper :: (Num a, Integral b, Eq a, Eq b) => a -> b -> b -> b
Step 6: Test the function
hyper 1 2 3 === 5
hyper 2 2 3 === 6
hyper 3 2 3 === 8
hyper 4 2 3 === 16
hyper 5 2 3 === 65536
hyper 6 2 3 === BIG#
Graham’s number is an extremely large number which can be expressed with the following function:
$$ g(n) = \left\{ \begin{array}{ll} 3 \uparrow\uparrow\uparrow\uparrow 3 & \text{if } n = 1 \\ 3 \uparrow^{g(n-1)} 3 & \text{if } n > 1, n \in \mathbb{N} \end{array} \right. $$
Graham’s number is then g(64)!
It is so large that if you filled every Planck volume with a digit in the observable universe it would not be enough to write it out!
But it can be expressed easily with the power of recursion!
How do we express it with our hyper
function?
Well the single up arrow is exponentiation, the double up arrow is tetration and so on. This is called Knuth’s up-arrow notation.
Thus, 4 up arrows (the base case), is hyper 6
since
hyper 3
is exponentiation.
Let’s try to define it:
graham :: Integer -> Integer
graham 1 = hyper 6 3 3
graham n = ...
What about the other case?
graham :: Integer -> Integer
graham 1 = hyper 6 3 3
graham n = hyper (graham (n - 1)) 3 3
Let’s try it:
> graham 1
And my computer exploded…
Let’s try to get a sense for just how big even the base case of this is:
$$ \begin{array}{lllllll} \footnotesize{3 \uparrow 3} & \footnotesize{=} & \footnotesize{3 * (3 * 3)} & \footnotesize{=} & \footnotesize{3 * 9} & \footnotesize{=} & \footnotesize{27} \\ \footnotesize{3 \uparrow\uparrow 3} & \footnotesize{=} & \footnotesize{3 ^ {3 ^ 3}} & \footnotesize{=} & \footnotesize{3 ^ {27}} & \footnotesize{=} & \footnotesize{7625597484987} \\ \footnotesize{3 \uparrow\uparrow\uparrow 3} & \footnotesize{=} & \footnotesize{3 \uparrow\uparrow (3 \uparrow\uparrow 3)} & \footnotesize{=} & \footnotesize{3 ^ {7625597484987}} & \footnotesize{=} & \footnotesize{\text{BIG}} \\ \footnotesize{3 \uparrow\uparrow\uparrow\uparrow 3} & \footnotesize{=} & \footnotesize{3 \uparrow\uparrow\uparrow (3 \uparrow\uparrow\uparrow 3)} & \footnotesize{=} & \footnotesize{3 \uparrow\uparrow\uparrow \text{BIG}} & \footnotesize{=} & \footnotesize{\text{REALLY BIG}} \\ \end{array} $$
And that is just the base case!!