[Agda] Induction-induction in a tricky order

Fredrik Nordvall Forsberg fredrik.nordvall-forsberg at strath.ac.uk
Tue Mar 15 17:08:34 CET 2016


Hi Simon,

On 15/03/16 14:06, Simon Boulier wrote:
> Is it a way to type check the following:
> 
>     data Foo  : Set
>     data Bar  : Foo → Set
>     data Bar2 : Foo → Set
> 
> [...]
>       bar'  : (x : Foo) → Bar (foo x (bar x) (bar2 x))
> [...]
>       bar2  : (x : Foo) → Bar2 x

So to summarise, you want a constructor for Bar which has a constructor
for Bar2 in its index. In this particular case, since Bar and Bar2 are
not dependent on each other, you can introduce them simultaneously by
indexing them over a two-element type, and then interleave the
constructors as you wanted to do. By defining syntactic sugar for Bar
and Bar2 early using new-style mutual blocks, we can make the definition
look quite natural:

-------------------------------------------------

open import Data.Bool

data Foo  : Set
data Bar-and-Bar2 : Foo → Bool → Set

Bar : Foo → Set
Bar x = Bar-and-Bar2 x true

Bar2 : Foo → Set
Bar2 x = Bar-and-Bar2 x false

data Foo where
  foo   : (x : Foo) → Bar x → Bar2 x → Foo

data Bar-and-Bar2 where
  bar   : (x : Foo) → Bar x
  bar2  : (x : Foo) → Bar2 x
  bar'  : (x : Foo) → Bar (foo x (bar x) (bar2 x))
  bar2' : (x : Foo) → Bar2 (foo x (bar x) (bar2 x))

-------------------------------------------------

If you want also Bar2 to be indexed over Bar, i.e.

  data Foo  : Set
  data Bar  : Foo → Set
  data Bar2 : (x : Foo) → Bar x → Set

this encoding does not work any more (without Hickey's Very Dependent
Types). However Agda lets us work around this by simultaneously defining
an auxiliary copy of the constructor in question (I learnt about this
trick from Ulf Norell on this mailing list):

  data Foo  : Set
  data Bar  : Foo → Set
  data Bar2 : (x : Foo) → Bar x → Set
  bar2aux : (x : Foo) → (y : Bar x) → Bar2 x y

This way earlier constructors know about the existence of bar2aux, and
after the real bar2 has been introduced, we can simply define
bar2aux = bar2. Note that this is *not* an instance of
(induction-)induction-recursion, but something weirder, because the
(trivially) recursively defined function bar2aux does not have as
codomain some already defined type D.

Here is your example again in this setting:

-------------------------------------------------

data Foo  : Set
data Bar  : Foo → Set
data Bar2 : (x : Foo) → Bar x → Set
bar2aux : (x : Foo) → (y : Bar x) → Bar2 x y

data Foo where
  foo   : (x : Foo) → (y : Bar x) → Bar2 x y → Foo

data Bar where
  bar   : (x : Foo) → Bar x
  bar'  : (x : Foo) → Bar (foo x (bar x) (bar2aux x (bar x)))

data Bar2 where
  bar2  : (x : Foo) → (y : Bar x) → Bar2 x y
  bar2' : (x : Foo) → Bar2 (foo x (bar x) (bar2aux x (bar x))) (bar' x)

bar2aux = bar2

bar'-has-right-type-because-this-is-accepted
  : (x : Foo) → Bar (foo x (bar x) (bar2 x (bar x)))
bar'-has-right-type-because-this-is-accepted = bar'

also-bar2'-has-right-type
  : (x : Foo) → Bar2 (foo x (bar x) (bar2 x (bar x))) (bar' x)
also-bar2'-has-right-type = bar2'

-------------------------------------------------

Semantically, it seems like it is possible to understand the meaning of
definitions like this (without the bar2aux encoding) in the same way
that we give semantics to the mutual dependencies of higher inductive types.

Hope that helps, and cheers,
Fredrik

-- 
Fredrik Nordvall Forsberg,
Department of Computer and Information Sciences,
University of Strathclyde.
The University of Strathclyde is a charitable body, registered in
Scotland, with registration number SC015263.


More information about the Agda mailing list