An Introduction to Fixpoint in Haskell Harold Lee harold at hotelling.net Introduction ------------ This is a short article that attempts to explain the concept of fixpoints to programmers learning the Haskell language. In particular, it is intended to help people who are stuck trying to understand the terse discussion of fixpoints in Paul Hudak's "The Haskell School of Expression" text. This is a literate Haskell source file, so it can be run - the code is prefixed with ">" at the beginning of the line. In Math ------- "fix" is the fixpoint operator. A fixpoint is a value for which a function returns the same value. For example, the fixpoint of > return1 = \x -> 1 is the value 1. return1 only has that one fixpoint, but functions can have more than one fixpoint, e.g. the identity function has all values as fixpoints: > identity = \x -> x To compute the set of fixpoints of a function (or even just one) you basically have two options: 1) Perform some sort of search of the domain of the function looking for fixpoint value(s). The search would likely be guided by constraining the function space that can be used. 2) Generate a proof based on stepwise algebraic transformation of the function into a composite of functions with known fixpoints. In Haskell ---------- Fixpoint behaves differently in Haskell compared to math when defined as below: > fix :: (a -> a) -> a > fix f = f $ fix f * note that this is the same as writing fix f = f (fix f) The type is the same as in math: it takes a function from a's to a's and returns an a (presumably for which f a = a). However, the way in which Haskell evaluates this definition of fix above is more limited than the mathematical definition. Basically, it takes a function and generates an infinite recursion of the function on itself. The important thing to remember is that Haskell will simply expand this ad infinitum, but lazily! So if try to compute a fix expansion, we're typically out of luck: fix myFunction = myFunction (fix myFunction) = myFunction (myFunction (fix myFunction)) = myFunction (myFunction (myFunction ( ... ))) But if the definition doesn't need to evaluate the (infinitely recursive) argument, then it is possible to get an answer. This works for the return1 function above because it ignores the argument: fix return1 = (\x -> 1) (fix myFunction) = the expression 1 with x bound to (fix myFunction) = 1 Note that the last step is possible because the binding of x is not referenced in the expression. For the identity function defined above, we're in trouble, because the expanded expression does use the binding of x. There is no way to find a value for the fix of identity that is not in terms of the argument. So (fix return1) is not very interesting because it stops recursively expanding right away, and (fix identity) is not useful to us because it recursively expands forever. The trick to using fix for defining your own recursive functions is to have it expand some number of times and then start collapsing. Defining Recursive Functions in Haskell --------------------------------------- You can use fix to define recursive functions more succinctly using the pattern that follows. We'll start with the all-time favorite introductory recursive function, factorial. > fact = fix helper > where helper f x | x <= 1 = 1 > helper f x = x * (f (x-1)) When x is 0 or 1, the expansion of the helper function ends right away, like it did with (fix return1). fact 1 = fix helper 1 = helper (fix helper) 1 = the expression 1 with f bound to (fix helper) = 1 When x is larger, then the helper expands using the second pattern, but it also decreases x with each expansion: fact 2 = fix helper 2 = helper (fix helper) 2 = 2 * (fix helper 1) = * for this step, refer to fact 1 above 2 * 1 = 1 This illustrates the need for laziness in this approach. At each step, the patterns for the definition of helper are checked, and an appropriate expansion is selected. Getting More General -------------------- We can expand this approach to solve the defining of recursive functions in general. The solution is made up of a helper function and the function you're writing, defined as the fix of the helper. The helper function should have arguments based on the function you're trying to create, plus an extra first argument so that it will work with fix. Applying that approach to the remainder function, which we want to give the remainder of one number modulo a second number. The type we want is Haskell is > remainder :: Integer -> Integer -> Integer The helper function will need to have an extra argument in the first position, as well as the two parameters for remainder. We lay out the base and inductive/recursive step in the helper, and fix does the rest. > remainder = fix helper > where helper f a b | a < b = a > helper f a b = f (a-b) b To wrap up, we can define gcd (greatest common divisor) using Euclid's clever algorithm in a fixpoint calculation. We can use the underscore (aka don't care) pattern match to make sure we don't accidentally use the helper's first argument in our base case. > gcd :: Integer -> Integer -> Integer > gcd = fix helper > where helper _ a b | b == 0 = a > helper f a b = f b (a `remainder` b) A good next step in understanding how all of this comes together is to derive the type of the helper function based on the type. of gcd and fix.