[Agda] Agda goes coinductive

Conor McBride conor at strictlypositive.org
Fri Jun 6 10:59:04 CEST 2008


Hi

On 5 Jun 2008, at 21:54, Dan Doel wrote:

> On Thursday 05 June 2008, Ulf Norell wrote:
>> One of the code sprints in the recent Agda meeting was to add  
>> coinductive
>> datatypes to Agda.
>
> Excellent! In celebration, here's everyone's favorite monad:  
> partiality (a mix
> of stuff from Abstract Nonsense for Functional Programmers and General
> Recursion via Coinductive Types).

Yes, this is splendid news! Well done! Wish I'd
been there, but I had drycleaning, ironing,
shaving, and punctuality obligations to
discharge...

And I was very pleased to see a Hancock classic
(with which, by the way, Coq has been known to
struggle) working out of the box. Of course, I'm
now hoping to understand the criteria for
accepting mutual definitions. There's plenty more
where that came from, so I'm confident the new
equipment will get lots thrown at it.

So now our troubles really begin...

[..]

> codata D (a : Set) : Set where
>   now   : a -> D a
>   later : D a -> D a
>
> never : {a : Set} -> D a
> never ~ later never

[..]

> codata _↑ {a : Set} : D a -> Set where
>   diverge : {dx : D a} -> dx ↑ -> later dx ↑
>
> -- problems here
> -- diverge-never : never ↑
> -- diverge-never ~ diverge diverge-never
> -- complaint: never != later never

Ulf's just remarked:

Ulf> This is the expected behaviour. When checking equality
Ulf> it's not safe to unfold corecursive functions.

[Later addition: what follows is an idea
which has tempted me for a while, but which
may, it turns out, have difficulties. Rather
than editing the text with hindsight, I thought
I'd just let you watch the crash...]

That's certainly the case for /evaluation/. As
some of you know, I suspect that one might try
a little bit harder in a type-directed equality
on normal forms. The intuition behind the
suggestion is that one should be able to tell
if two normal forms constitute "intensionally
the same computation in different stages of
evolution" by running the /initially/ less
involved one for finite time.

That's to say, when faced with deciding whether

   ne == cocon (cocon ... (ut)) : CoDataType

where neither ne nor ut is coconstructor-headed
(substitute whatever coalgebraic notion is
actually in place); note, there may be multiple
ut's.

repeatedly unfold ne until one of 4 things
inevitably happens

(1) ne fails to evolve a coconstructor
because of some inner neutral thing gumming
up the works, so say NO
(2) the coconstructors emerging from ne
conspicuously differ from those around ut,
in which case say NO
(3) ne evolves to the same pattern of
coconstructors, wrapped around some neutral
ne', in which case compare ne' with ut
as neutral values
(4) in some step, the coconstructors from
ne pass leapfrog those around ut, so that
the left-hand side is strictly more evolved
than the right, hence they cannot come
from the same mechanism, so say NO

Crucially, we decide up front which side to
evolve: we don't race the two. Moreover,
we're not trying to find a /simulation/,
just a more liberal /intensional/ match.
So if you define new constants

   fred ~ C fred
   jim ~ C jim

you get

   fred == C fred
   fred /= jim

(Of course, if you were to elaborate fred
and jim in terms of some universal unfold,
then they would become equal, but that's
under at least a few laters.)

Key questions:

(1) Does this loop?
   I don't think so. Productivity of ne
   guarantees that the tree of
   coconstructors evolved increases strictly,
   towards a bound given by the tree of
   coconstructors around the ut's. We
   continue either by saying no, or by
   comparing neutral terms.
(2) Is it a congruence? I'm not sure. It
   seems stable with respect to further
   evolution. I guess we also need to check
   that if ne and ne' evolve to the same
   coconstructory agglomeration, then ne
   and ne' coincide. That's a worry:
   what about

     fred ~ C fred
     jim  ~ C fred

   ? Maybe there's too much generativity
   around to get away with this approach.
   Maybe something more fundamental is
   wrong. Maybe we can recognize the
   distinction between evolving recursive
   fred and non-recursive jim.
(3) Is this too intensionally brittle to
   be helpful? My hope is that making ~
   imply == might save a lot of basic
   misery. But if generativity introduces
   too many unexpected distinctions, we
   might consider ourselves worse off.

Of course, if it's too brittle to be a
congruence, then it's no good, so we need
to record it as a mistake from which to
learn.

Perhaps it's hubristic to try to squeeze
a bit more out of the definitional equality
here. These are infinite objects, so
equational reasoning is inevitably going to
be evidence-based. A decent implementation
of coinduction is just what we need to bang
the last few nails into the coffin of
intensional propositional equality...

All the best

Conor
>



More information about the Agda mailing list