Functions are values, so they need types too - and a function type is the first type with structure inside it. Today you add the arrow type and teach equality and printing to look inside it.
Represent function types as arrows and compare and print them structurally.
A function has a type just like a number does, and it is the first type with other
types inside it: a function from Int to Bool has type Int -> Bool. This
is the arrow type, and adding it means Equal can no longer just compare tags -
two arrows are equal only when their argument types match and their result types
match, which is a recursive comparison. That recursion is the seed of unification a
few chapters from now.
Printing arrows introduces one convention worth fixing early: the arrow
associates to the right, so Int -> Bool -> Bool always means
Int -> (Bool -> Bool), the type of a function that takes an Int and returns
another function. Because of that, only a left side that is itself an arrow
needs parentheses to stay unambiguous - (Int -> Bool) -> Bool is a genuinely
different type, a function that takes a function. Getting this printing right now
pays off at the end, when every inferred type is shown back to the user.
// an arrow type carries two types: what it takes and what it returns.type TArrow struct{ From, To Type }// Equal must now recurse into both sides.// String prints right-associatively: the arrow groups to the RIGHT,// so only a From that is itself an arrow needs parentheses.func (t TArrow) String() string { /* "From -> To", paren From if arrow */ }