[Agda] Recursive instance search

Guillaume Brunerie guillaume.brunerie at gmail.com
Fri Jul 11 12:26:58 CEST 2014


Hello all,

As some of you know, I started working on recursive instance search
during the previous AIM and the code is now available on my GitHub
here : https://github.com/guillaumebrunerie/agda (should I open a pull
request against the official Agda?)

The main outstanding issue is that there is no termination check yet
so if you’re not careful when defining your instances you can easily
make Agda loop.
Also, I haven’t really thought about namespace management, for
instance 'hiding' does not hide instances (but serialization should
work, though).

I copy-pasted the relevant part of the CHANGELOG below (I borrowed
part of the text in issue 938) and you can also look at the two files
test/succeed/RecursiveInstanceSearch{,Level}.agda to see some more
advanced examples of recursive instance search.

Of course I welcome any feedback/bug reports.

Cheers,
Guillaume




* Instance search is now more efficient and recursive (see issue 938)
  (but without termination check yet)

  A new keyword `instance' has been introduced (in the style of
  `abstract' and  `private') which must now be used for every
  definition/postulate that has to be taken into account during instance
  resolution. For example:

    {-# OPTIONS --copatterns #-}

    record RawMonoid (A : Set) : Set where
      field
        nil  : A
        _++_ : A -> A -> A
    open RawMonoid

    instance
      rawMonoidList : {A : Set} -> RawMonoid (List A)
      rawMonoidList = record { nil = []; _++_ = List._++_ }

      -- using copatterns to define this recursive instance:

      rawMonoidMaybe : {A : Set} {{m : RawMonoid A}} -> RawMonoid (Maybe A)
      nil rawMonoidMaybe = nothing
      _++_ rawMonoidMaybe nothing mb = mb
      _++_ rawMonoidMaybe ma nothing = ma
      _++_ (rawMonoidMaybe {{m}}) (just a) (just b) = just (_++_ m a b)

  Moreover, each type of an instance must end in (something that reduces
  to) a named type (e.g. a record, a datatype or a postulate). This
  allows us to build a simple index structure

    data/record name  -->  possible instances

  that speeds up instance search.

  Instance search takes into account all local bindings and all global
  'instance' bindings and the search is recursive. For instance,
  searching for

    ? : RawMonoid (Maybe (List A))

  will consider the candidates {rawMonoidList, rawMonoidMaybe}, fail to
  unify the first one, succeeding with the second one

    ? = rawMonoidMaybe {A = List A} {{m = ?m}} : RawMonoid (Maybe (List A))

  and continue with goal

    ?m : RawMonoid (List A)

  This will then find

    ?m = rawMonoidList {A = A}

  and putting together we have the solution.

  Be careful that there is no termination check for now, you can easily
  make Agda loop by declaring the identity function as an instance. But
  it shouldn’t be possible to make Agda loop by only declaring
  structurally recursive instances (whatever that means).

  Additionally:

  * Uniqueness of instances is up to definitional equality (see issue 899)

  * Instance search will not be attempted if there are unsolved metas in
    the goal. The issue is that trying to typecheck [? ++ ?] would
    generate a constraint of type `RawMonoid ?' to be solved by instance
    search, but unification with `rawMonoidMaybe' will succeed and
    generate a new constraint of type `RawMonoid ?2' to be solved by
    instance search, and so on, so Agda would loop.

    As a special case, if none of the instances declared for that type
    is recursive, then instance search is attempted as usual even if
    there are unsolved metas (no risk of looping here).

  * Instances of the following form are allowed:

        Π-level : {n : ℕ} {A : Set} {B : A → Set}
                  {{_ : (a : A) → has-level n (B a)}}
                  → has-level n ((a : A) → B a)

      When searching recursively for an instance of type `(a : A) →
      has-level n (B a)', a lambda will automatically be introduced and
      instance search will search for something of type `has-level n (B
      a)' in the context extended by `a : A'.

  * There is no longer any attempt to solve irrelevant metas by instance
    search

  * Constructors of records and datatypes are automatically added to the
    instance table


More information about the Agda mailing list