Instance search limitations

我与影子孤独终老i 提交于 2019-12-06 07:28:57

Removing ambiguity

If we typecheck this:

open import Category.Functor
open import Category.Monad
open RawFunctor
open RawMonad

and run C-c C-w _<$>_ (C-c C-w is "explain why a particular name in scope"), we get (after some cleaning)

_<$>_ is in scope as
  * a record field Category.Functor.RawFunctor._<$>_
  * a record field Category.Monad.RawMonad._._<$>_

I.e. _<$>_ is ambiguous, so it's cumbersome to use monads and functors, that are not monads, in the same module, because you have to manually disambiguate between two _<$>_.

This can be cured with instance arguments. Instead of opening Functor in the definition of Monad (via Applicatve):

record RawIMonad ... where

  open RawIApplicative rawIApplicative public

we can provide an instance and let instance search do the job (the definitions of Applicative and Functor can be found here):

record Monad {α} (M : Set α -> Set α) : Set (suc α) where
  infixl 1 _>>=_

  field
    return : ∀ {A} -> A -> M A
    _>>=_  : ∀ {A B} -> M A -> (A -> M B) -> M B

  instance
    Monad<:Applicative : Applicative M
    Monad<:Applicative = record { pure = return ; _<*>_ = λ mf mx -> mf >>= λ f -> mx >>= return ∘ f }
open Monad {{...}}

Now there is only one _<$>_ — in the definition of Functor, but instance search sees, that a monad is an applicative and an applicative is a functor, so _<$>_ is defined on monads, because it's defined on functors.

Instance fields

At the moment you can't declare fields of records as instances:

record R : Set where
  field
    instance n : ℕ

The workaround is

record R : Set where
  field
    n : ℕ

   instance
     R->ℕ : ℕ
     R->ℕ = n

Weakness

Instance search doesn't cooperate with metavariables resolution.

instance
  fz : Fin 1
  fz = zero

z : ∀ {n} {{_ : Fin n}} -> ℕ
z = 0

yellow : z ≡ 0
yellow = refl

ok : z {1} ≡ 0
ok = refl

In yellow instance search doesn't find the fz instance. I was told, that this is the intended behaviour, but it looks too restrictive to me and I don't see any benefits.

One workaround is to use instance arguments in place of implicit arguments:

instance
  one : ℕ
  one = 1

  fz : Fin 1
  fz = zero

  z : ∀ {{n}} {{_ : Fin n}} -> ℕ
  z = 0

  now-ok : z ≡ 0
  now-ok = refl

Instances are always imported

module M where
  instance
    z : ℕ
    z = 0

z' : {{n : ℕ}} -> ℕ
z' {{n}} = n

ok : z' ≡ 0
ok = refl

The M module is not opened, but the instance is in scope. If you want to hide instances, use records:

record R : Set where
  instance
    z : ℕ
    z = 0

z' : {{n : ℕ}} -> ℕ
z' {{n}} = n

error : z' ≡ 0
error = refl

open R _

ok : z' ≡ 0
ok = refl

A nasty bug

We can rewrite ok as

ok : let open R _ in z' ≡ 0
ok = refl

But if define ok' below

ok' : z' ≡ 0
ok' = refl

The instance from R is not in scope, but Agda picks it anyway. The same holds for the value level. I.e. if you import a module or open a record, instances from it will be availaible for all definitions below, regardless of where you opened it.

Personal experience

I was fighting with instance arguments for two weeks or so, trying to implement some basic category theory in Agda, but instance search is unpredictable because of its weakness — adding a parameter to a record ruins everything. It's also hard to figure why everything is yellow — is it because you are doing something silly or because Agda refuses to resolve a trivial metavariable? When you have a type signature on six lines and several nested records, it's a matter of luck, whether you'll find a way to overcome instance search limitations or not.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!