Mutual Avoidance

Sometimes dependent types push you towards mutually defined data structures: I try to avoid them where I can. For me, this occurrs when you need a helper data type where a container is not suitable expressive at the type level to capture the inductive case. When this happens you can index your helper data structure with the signature of the top type. This helps remove the need for a mutual definition, that is turn the call graph edge from undirected to directed.

Here we illustrate the problem and provide a nice solution.

Preliminaries

First we define some helper containers that exist outside of the example in public libraries but we include here to keep the example self-contained.

A reformulation of a vector of dependent pairs. Analogous to the forall list quantifier.

A reformulation of a vector of dependent pairs but with an added constraint on the parameter/index. Analogous to the forall list quantifier.

A reformulation of a vector of dependent pairs. Analogous to the forall list quantifier.

The Setting

Here we provide the setting in which our problem and solution must exist.

Meta typing

Some important information

Types

Context

We define a collection of types as follows.

The Problem

Our problem comes from wanting to establish a predicate over a given instance of Types such that all instances of Bravo are set to true.

We will call this predicate: AllTrueBravos.

The interesting case is not defining a predicate over Types but over a single instance of Type. The interesting case is for `Charlie.

We will call the predicate on a single Type instance: AllTrueBravo.

We begin by defining an instance for AllTrueBravo to see what the problem is.

Attempt One

A Question: What should the type of ?attempOneRhs be?

Ideally, we should want to use an instance of VectP as that gives us the type:

It type checks! Ship It!

No!

The problem is the unaccounted for foo. When you go to write your decidable procedure, this will cause a unification problem trying to assert that some foo is equal to another foo, and thus two bravo’s that you know are the same cannot be shown to be the same.

Attempt Two: A Little Miss-direction can ‘help’

Let us attemp to fix the problem by spinning out a definition to capture BravoTrue instances for the CharlieTrue case.

Here we will need to define a mutual block to capture two mutually defined data structures.

It type checks! Ship It!

No!

We have the same problem as before.

When you go to write your decidable procedure, the miss direction will cause a unification problem when working on the ApplyLMD pattern for the CharlieTrue cases.

We need to ensure that the ‘proofs’ on cs are applied to cs and that there is no ambiguity. Unificiation will be problematic otherwise.

Attempt Three: A Little Miss-direction can ‘help’ Take 2

Let us attemp to fix the problem by not using VectP and using a custom data structrure.

However, we are still in a mutual block. Let us parameterise LittleMissDirect by the type signature of AllTrueBravoD to allow us to remove the need for a mutual block.

Attempt Four: The Rug that brings it all together

Note

When writing the decision procedure for littleMissDirect you will want to first define the procedure signature for allTrueBravo first.

and then realise littleMissDirect.

and then give the procedure body for allTrueBravos.

Thus, further removing the need for a mutual block, as you will need allTrueBravo.

Not to mention that last of all you will want to give the decision procedure for allTrueBravos.