Random stuff

Rabbits make up history on demand

Operations defined in the previous post are not total. For example, we can’t go up from the root of the tree. That’s because root element has no history. Let’s pretend that this whole tree is just a subtree of a tree with the same uniform structure that spans infinitely in all directions (including up).

In other words, when we need to go up from an element that has no up, we will imagine fictitious chain of events that brought this element into existence.

data RabbitHistory =
    Born RabbitHistory |
    Stayed RabbitHistory |
    Matured RabbitHistory |
    ImaginaryHistory Int

Root element is denoted

ImaginaryHistory 0

but when necessary, we will also treat it as

Matured (Born (Stayed (ImaginaryHistory 1))),

where integer value is used to keep track of how much history we imagined.

imagineHistory :: RabbitHistory -> RabbitHistory
imagineHistory (ImaginaryHistory k) =
    Matured (Born (Stayed (ImaginaryHistory (k + 1))))

Amended implementation of up adds additional “catch all” clause in case regular patterns do not match (there is no regular up):

up :: RabbitHistory -> RabbitHistory
up (Born h) = h
up (Stayed h) = h
up (Matured h) = h
up h = up (imagineHistory h)  -- no real history

Let’s consider what happens if we go up and then down from the root: down (up (ImaginaryHistory 0)) first reduces to down (up (Matured (Born (Stayed (ImaginaryHistory 1))))), then to down (Born (Stayed (ImaginaryHistory 1))), and finally to [Matured (Born (Stayed (ImaginaryHistory 1)))], which actually should be just [ImaginaryHistory 0].

This is unfortunate, because we loose the uniqueness property of our element representation. To get it back, we will replace direct calls to Matured constructor with calls to matured helper that automatically collapses imaginary part whenever possible:

matured :: RabbitHistory -> RabbitHistory
matured (Born (Stayed (ImaginaryHistory k))) | k > 0 =
    ImaginaryHistory (k - 1)
matured h = Matured h

Implementation of down will now look like this (one character edit):

down :: RabbitHistory -> [RabbitHistory]
down (Born h) = [matured (Born h)]
down h = [Stayed h, Born h]

To update left and right, we will do both (add “catch all” part and replace Matured with matured):

left :: RabbitHistory -> RabbitHistory
left (Born h)           = Stayed h
left (Matured (Born h)) = Born (Stayed h)
left (Stayed h)         = rightChild (left h)
    where rightChild h = last (down h)
left h = left (imagineHistory h)  -- no real history

right :: RabbitHistory -> RabbitHistory
right (Stayed h)        = Born h
right (Born (Stayed h)) = matured (Born h)
right (Born h)          = Stayed (right h)
right (Matured h)       = Stayed (right h)
right h = right (imagineHistory h)  -- no real history

Now up, down, left, and right are defined on all elements reachable from the root.