Stabilizing Values

haskell

Just recently I discovered a simple solution to an issue I encountered when developing parts of my directed-cubical library. A very common pattern in the code was to apply a function f: a -> a repeatedly to an initial value x until the value stabilized, or in other words to find a fixed point. The type signature of such a function looks something like

stabilize :: (Eq a) => (a -> a) -> a -> a  

where the first argument is the function and the second argument is the initial value. At first I figured that this must have something to do with our old friend

fix :: (a -> a) -> a

but the presence of an initial value suggests that we can’t derive the answer just by supplying some function. Recall that we use fix essentially by unfolding the fixed point’s value based on whatever we know about it already. However, in the case of “stabilize” we might be interested in a strict function f, and there might be multiple fixed points of interest based on the choice of initial point. Fortunately there’s this lovely little function in the Haskell Prelude:

until :: (a -> Bool) -> (a -> a) -> a -> a
until p f = go
  where
    go x | p x          = x
         | otherwise    = go (f x)

Using this, we can define stabilize as follows:

stabilize :: (Eq a) => (a -> a) -> a -> a
stabilize f x = until (\x -> x == f x) f x

This is nice and succinct, but observe that whenever we apply the predicate p in the definition of until we compute f x, only to throw it away and recompute it when recursing! Since until has no knowledge of our predicate we end up evaluating f x values twice as often as we need to. However, this points us in the right direction: what we really want is the list of all iterates of f on the initial value x all at once, and this is provided by our friend iterate:

iterate :: (a -> a) -> a -> [a]

Given the list of all the iterates, we just need to pair each iterate with the next (that’s a zip!) and search for the first pair (a,b) where a == b, but this is easy:

stabilize' :: (Eq a) => (a -> a) -> a -> a
stabilize' f x = fst . head . dropWhile (uncurry (/=)) $
                 zip fxs (tail fxs)
   where fxs = iterate f x

This is a nice illustration of the utility of uncurry. The presence of Eq is also important here: technically list types [a] shouldn’t belong to this type class because comparing infinite lists for equality doesn’t terminate! (and this easily results in problems in ghci). So Eq a is actually making a strong assumption about our type a. We can partly recover fix with the following definition:

fix' :: (Eq a) => (a -> a) -> a
fix' f = stabilize' f (fix' f)

The fix' f part is us cheating to produce some element of a out of thin air. Alas, to apply this we need a type a that is an honest instance of Eq as well as supporting a least fixed point. I suppose infinite terms in a type with Eq based on observable sharing would fit the bill, but I haven’t gone down that road yet.