[Agda] Tentative guidelines for working with coinductive types

Nils Anders Danielsson nad at Cs.Nott.AC.UK
Thu Jan 15 03:23:48 CET 2009


On 2009-01-14 20:36, Noam Zeilberger wrote:
> 
> My one thought about the proposed changes to Agda is that, while
> they do make good sense, it might end up being easier and less
> bug-prone to adopt the more general, destructor-based view of
> coinductive types.

I think that the simplest implementation of the destructor-based view
would be almost identical to what I suggested.

In the simplest case we can settle for a single "corecord" type,
analogously to the codata type which I suggested, and almost identical
to the "coalg" type which Anton suggested in "Getting codata right":

   corecord ∞ (A : Set) : Set where
     ♭ : A

We can then define the "constructor" ♯_:

   ♯_ : ∀ {A} → A → ∞ A
   ♭ (♯ x) = x

Streams and stream equality can be defined as follows, just as in the
constructor-based approach:

   data Stream (A : Set) : Set where
     _∷_ : A → ∞ (Stream A) → Stream A

   data _≈_ {A} : Stream A → Stream A → Set where
     _∷_ : ∀ x {xs ys} → ∞ (♭ xs ≈ ♭ ys) → x ∷ xs ≈ x ∷ ys

We can also define an infinite stream of ones:

   mutual

     ones : Stream ℕ
     ones = 1 ∷ ones′

     ones′ : ∞ (Stream ℕ)
     ♭ ones′ = ones

Let us now see if we can prove that ones is equal to its own unfolding
(almost) with the simple corecursive proof that did not work for "inf"
in another message in this thread:

   eq : ones ≈ 1 ∷ ♯ ones
   eq = 1 ∷ eq′
     where
     eq′ : ∞ (♭ rec ≈ ♭ (♯ones))
     ♭ eq′ = ?

With the simplest implementation of evaluation and equality (i.e. more
or less the one which is already implemented) the goal type evaluates to

   1 ∷ ones′ ≈ 1 ∷ ones′,

which can be proved using reflexivity, but not using a corecursive call
to eq:

   eq : 1 ∷ ones′ ≈ 1 ∷ ♯ (1 ∷ ones′).

This mirrors the current situation exactly.

By generalising wildly from this simple example it seems to me as if the
destructor-based view is completely equivalent to the constructor-based
one, as long as the ∞-approach is used. In this setting the issueof
definitional equality seems to be orthogonal to the issue of
constructor vs. destructor.

I should perhaps mention that you can have something very similar to the
destructor-based view already now (I had not realised that before):

   record Stream (A : Set) : Set where
     field
       head : A
       tail : ∞ (Stream A)

   open Stream

   map : ∀ {A B} → (A → B) → Stream A → Stream B
   map f xs = record { head = f (head xs)
                     ; tail = map′
                     }
    where map′ ~ ♯ map f (♭ (tail xs))

This is accepted by Agda now. With my suggested changes to Agda the code
would be nicer:

   map : ∀ {A B} → (A → B) → Stream A → Stream B
   map f xs = record { head = f (head xs)
                     ; tail = ♯ map f (♭ (tail xs))
                     }

-- 
/NAD




This message has been checked for viruses but the contents of an attachment
may still contain software viruses, which could damage your computer system:
you are advised to perform your own checks. Email communications with the
University of Nottingham may be monitored as permitted by UK legislation.



More information about the Agda mailing list