[Agda] Avoiding implicit argument proliferation?
Brandon Moore
brandon_m_moore at yahoo.com
Fri Mar 25 14:34:58 CET 2011
On 2011-03-25 04:14 Nils Anders Danielsson <nad at chalmers.se> wrote:
> On 2011-03-23 08:00, Brandon Moore wrote:
> > Here's some code, extracted from an attempt at formalizing early excercies
> > in "Categories for the Working Mathematician". The hidden arguments
> > continue to accumulate as higher structures are build. They can be
inferred,
> > so working with these definitions in expressions is not too bad, but to
>state
> > any types it seems I need to quantify over (and manually provide) all these
> > implicit arguments.
>
> See the following thread for a previous discussion about this issue:
>
> Why aren't free vars in types automatically generalized
> http://thread.gmane.org/gmane.comp.lang.agda/772
Thanks for the reference. Unfortunately, it seems light on suggestions
for working with the present language. I do have some ideas for automatically
quantifying over implicit parameters that might work a bit better than the
ones mentioned in that thread, but there are still some problems to work out.
Anyway, I was hoping for suggestions that work with the current language.
I have found a partial solution. At least, it works on small examples; I haven't
converted the whole program yet. In short, make a module which rewrites
all the record components according to an equality, and use that.
My problem isn't that I mind explicitly quantifying over implicit parameters,
it is the they types of those parameters have parameters, and those parameters
have types with parameters and so on.
Working with records containing types avoids having to pass these parameters,
and works great as long as the types can be independent, but I lacked a good
approach for constraining two records to agree on a type component.
For a simple example, take a record with a function between two sets
record Fun : Set₁ where
field
A B : Set
f : A → B
Defining a commutative square is easy:
record Square : Set₁ where
field
F G : Fun
top : Fun.A F → Fun.A G
bottom : Fun.B F → Fun.B G
commute : (x : Fun.A F) → Fun.f G (top x) ≡ bottom (Fun.f F x)
Working with records that are supposed to agree is harder.
compose : (R1 R2 : Fun) → Fun.B R1 ≡ Fun.A R2 → Fun
compose R1 R1 eq = ?
Rewriting by eq has no effect, and I can't match on it:
Cannot pattern match on constructor refl, since the inferred
indices [Fun.B R3] cannot be unified with the expected indices
[Fun.A R4] for some R4, R3
when checking that the expression ? has type Fun
Opening modules in a where clause provides shorter names,
but the values are still not sufficiently in scope to be improved
by a rewrite - this will not refine:
compose2 : (R1 R2 : Fun) → Fun.B R1 ≡ Fun.A R2 → Fun
compose2 R1 R2 eq = record { A = A; B = C; f = fg }
where
open Fun R1
open Fun R2 renaming (A to B1 ; B to C; f to g)
fg : A → C
fg rewrite eq = {! g ∘ f !}
It seems the only option is to explicitly repeat every record
field the definition wishes to use in an with-clause to bring it into
the local scope, so it can be refined:
compose3 : (R1 R2 : Fun) → Fun.B R1 ≡ Fun.A R2 → Fun
compose3 R1 R2 eq with Fun.f R1 | Fun.f R2
... | f | g rewrite eq = record { f = g ∘ f }
Overhead scaling with the number of times a field is mentioned
is unacceptable. Fortunately, I realized I can put the casting
in a module (I expect this is where someone is going to point out that
it's already generated automatically, or at least demonstrated a few times
in the standard library):
module CastFun (r : Fun){A' : Set}(cdom : Fun.A r ≡ A')
{B' : Set}(ccod : Fun.B r ≡ B') where
A : Set
A = A'
B : Set
B = B'
f : A → B
f with Fun.f r
f | f' rewrite cdom | ccod = f'
This allows a record to be opened under equalities
compose4 : (R1 R2 : Fun) → Fun.B R1 ≡ Fun.A R2 → Fun
compose4 R1 R2 match = record { f = g ∘ f }
where
open CastFun R1 refl match
open Fun R2 hiding (A) renaming (B to C; f to g)
I'll see how well this works for the full program.
Brandon
More information about the Agda
mailing list