5

I was reading Jeremy Gibbons' article on origami programming and I got stuck on exercise 3.7, which asks the reader to prove the fusion law for list unfolds:

unfoldL p f g . h = unfoldL p' f' g'

if

p . h = p'
f . h = f'
g . h = h . g'

The function unfoldL, unfold for lists, is defined as follows:

unfoldL :: (b -> Bool) -> (b -> a) -> (b -> b) -> b -> List a
unfoldL p f g b = if p b then Nil else Cons (f b) (unfoldL p f g (g b))

Here is my current attempt at a proof:

(unfoldL p f g . h) b
=   { composition }
unfoldL p f g (h b)
=   { unfoldL }
if p (h b) then Nil else Cons (f (h b)) (unfoldL p f g (g (h b)))
=   { composition }
if (p . h) b then Nil else Cons ((f . h) b) (unfoldL p f g ((g . h) b))
=   { assumptions }
if p' b then Nil else Cons (f' b) (unfoldL p f g ((h . g') b))
=   { composition }
if p' b then Nil else Cons (f' b) ((unfoldL p f g . h) (g' b))
=   { ??? }
if p' b then Nil else Cons (f' b) (unfoldL p' f' g' (g' b))
=   { unFoldL }
unFoldL p' f' g'

I am not sure how to justify the step marked with ???. I should probably use some sort of induction on function application on b? Related question: what are some good resources that explain and motivate various induction techniques, such as structural induction?

luqui
  • 59,485
  • 12
  • 145
  • 204
Dan Oneață
  • 968
  • 7
  • 14

2 Answers2

3

I haven't read Gibbons's article, and there might be other techniques that he's relying on, but here's what I know.

It's a good intuition that you are looking for some kind of induction, because what you need is very similar to what you are trying to prove. But you actually need coinduction here. It's because unfoldL can produce infinite lists. In formal type systems, it's very rare that you can prove that two infinite structures are "equal" -- formal equality is much too strong to prove most results. Instead, we prove bisimilarity, which in informal circumstances might as well be equality.

Bisimilarity is tricky theoretically, and I won't go into it (partially because I don't fully understand the underpinnings), but working with it in practice is not too hard. Basically, to prove that two lists are bisimilar, you prove that they are either both Nil, or that they are both Cons with the same head and their tails are bisimilar, and you may use the coinductive hypothesis when proving the bisimilarity of the tails (but not elsewhere).

So for you, we prove by coinduction that for all b (because we need to use the coinductive hypothesis for different bs):

(unfoldL p f g . h) b ~~ unfoldL p' f' g' b

So far we have

(unfoldL p f g . h) b
= { your reasoning }
if p' b then Nil else Cons (f' b) ((unfoldL p f g . h) (g' b))

Analyzing by cases on p' b, if p' b is True then

if p' b then Nil else Cons (f' b) ((unfoldL p f g . h) (g' b))
= { p' b is True }
Nil
~~ { reflexivity }
Nil
= { p' b is True }
if p' b then Nil else Cons (f' b) (unfoldL p' f' g' (g' b))
= { unfoldL }
unfoldL p' f' g'

; if p' b is False then

if p' b then Nil else Cons (f' b) ((unfoldL p f g . h) (g' b))
= { p' b is False }
Cons (f' b) ((unfoldL p f g . h) (g' b))
*** ~~ { bisimilarity Cons rule, coinductive hypothesis } ***
Cons (f' b) (unfoldL p' f' g' (g' b))
= { p' b is False }
if p' b then Nil else Cons (f' b) (unfoldL p' f' g' (g' b))
= { unfoldL }

The line marked with *** is the key one. First, note it's a ~~ instead of an =. Also, we were allowed to use the coinductive hypothesis only underneath the Cons constructor. If we were allowed to use it anywhere else, then we could prove that any two lists are bisimilar.

luqui
  • 59,485
  • 12
  • 145
  • 204
  • 1
    Why is bisimulation necessary here? What is wrong with the definition that two lists are equal if they have the same elements in the same order? It seems to me this definition works for infinite lists. The two steps here involving `~~` seem to hold for `=` as well - `Nil = Nil` and `x = y => Cons a x = Cons a y`. Also, why reason by cases? You could apply the induction hypothesis directly under `\a -> if p' b then Nil else Cons (f' b) a` since that is a constructor guarded term (i.e. by applying congruence to the induction hypothesis for a large function than `Cons ..`). – user2407038 Aug 14 '17 at 00:23
  • @user2407038, "have the same elements in the same order" *is* what bisimulation means. And there's nothing contradictory about the axiom "bisimilarity implies equality", but it is after all an axiom -- it doesn't follow from the lambda calculus laws -- and we should know what we're doing. – luqui Aug 14 '17 at 00:54
  • @user2407038, you're right about the steps involving equality, but we would still need to say "if `Nil = Nil` or `x = y => Cons a x = Cons a y` holds at every position, then the lists are equal", that is, coinduction. And it is in the attempt to say that rigorously that we arrive at bisimilarity. – luqui Aug 14 '17 at 00:58
  • @user2407038, the case analysis is again related to this separation between bisimilarity and equality, because we don't *a priori* have a rule that says "if `xs ~~ ys` then `f xs ~~ f ys`" -- and there are consistent worlds in which this is not the case, in which `f` can inspect something about its argument that is beyond its elements (I can't think of any computable ones though and my hunch says there aren't any). It's reasonable to require all functions to be referentially transparent to infinite structures like this, but it is not a given and must be stated axiomatically. – luqui Aug 14 '17 at 01:14
  • @user2407038, I'm approaching all this from the perspective of my experience in type theory. I haven't thought much about it, but I'm guessing if we reasoned at the level of Haskell's denotational semantics as a Scott domain then you'd be right and equality would do the job (we'd still have to figure out the precise conditions of (co)induction). I just don't have experience with this kind of reasoning. – luqui Aug 14 '17 at 01:19
  • ""have the same elements in the same order" is what bisimulation means" - is this not the definition of equality? Or does 'bisimulation' mean 'pointwise lifting of propositional equality to coinductive lists' (which is not the same as propositional equality for coinductive lists. Whereas for finite lists, the pointwise lifting of propositional equality and propositional equality are isomorphic. Does 'formal equality' mean propositional equality?). If this understanding is correct, then it answers my questions (but the wiki page for bisimulation doesn't seem to reflect this understanding...) – user2407038 Aug 14 '17 at 01:50
  • @user2407038, yes it seems like we are in agreement starting with "Or" :-), Yeah the wiki page doesn't really talk about any of these issues... I don't know of a good reference. – luqui Aug 14 '17 at 03:13
  • Thanks for the answer and for the discussion, which is a bit over my head :-) A couple of points I would like to clarify: (i) When you defined bisimilarity I think you forgot to mention that the heads of the lists have to be equal for the `Cons` case, right? (ii) Just to make sure, is this the coinductive hypothesis: `unfoldL p f g (g (h b)) ~~ unfoldL p' f' g' (g' b)`? – Dan Oneață Aug 14 '17 at 08:14
  • 2
    @DanOneață, thanks, fixed. As for (ii), it's slightly more general than that: *for all `b`*, `unfoldL p f g b ~~ unfoldL p' f' g' b`. it's exactly the same as what we are trying to prove initially about the whole list, except that it must be applied to the tail of `Cons`. Note the symmetry with induction, where we get to assume it's true about the tail in order to use it on the whole list. – luqui Aug 14 '17 at 11:14
2

After some googling I've found a paper by the same Jeremy Gibbons (and Graham Hutton) that presents proof methods for corecursive programs; the article includes a section on bisimulation and coinduction, the proof technique used by @luqui in the accepted answer. Section 6 has a proof of the fusion law using the universal property of unfold. For completeness, I've copied the proof below.


The universal property of unfoldL:

h = unfoldL p f g
<=>
∀ b . h b = if p b then Nil else Cons (f b) (h (g b))

Proof of the fusion law:

unfoldL p f g . h = unfoldL p' f' g'
<=> { universal property }
∀ b . (unfoldL p f g . h) b = if p' b then Nil else Cons (f' b) ((unfoldL p f g . h) (g' b))
<=> { composition }
∀ b . unfoldL p f g (h b) = if p' b then Nil else Cons (f' b) (unfoldL p f g (h (g' b)))
<=> { unfoldL }
∀ b . if p (h b) then Nil else Cons (p (h b)) (unfoldL p f g (g (h b))) = if p' b then Nil else Cons (f' b) (unfoldL p f g (h (g' b)))
<=> { composition }
∀ b . if (p . h) b then Nil else Cons (p . h) b (unfoldL p f g ((g . h) b)) = if p' b then Nil else Cons (f' b) (unfoldL p f g ((h . g') b))
<=  { assumptions }
(p . h = p') ^ (f . h = f') ^ (g . h = h . g')
Dan Oneață
  • 968
  • 7
  • 14