问题
The instance arguments machinery is described in an old paper and at the Agda wiki. Are there some notable facts that these sources do not mention? What are limitations of instance search?
回答1:
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.
来源:https://stackoverflow.com/questions/31544734/instance-search-limitations