[Agda] Implicit arguments

effectfully effectfully at gmail.com
Mon Apr 13 01:16:38 CEST 2015


Disclaimer: I'll desribe my intuition. I have no idea what the actual rules are.

1) Agda can infer `x' from (f x), if (not iff) there is no
pattern-matching in `f'. Agda can infer

  1.1) `A' and `n' from (Vec A n)
  1.2) `n' from (Fin (suc n)).
         It's always possible to infer `x' from (f x), if `f' is a
sequence of constructors.
  1.3) `m' from (Fin (n + m)), if `n' is known.
         It makes perfect sense, because `_+_' is not injective,
         and hence there are various ways to instantiate `n' and `m'.
         But after you have instantiated `n', (n + m) reduces to (suc
(suc ... m)), which is the 1.2 case.
         I.e. you have to explicitly pass arguments, if there is
pattern-matching on them (there is an exception, see below). Examples:

    postulate
      fancy₁ : ∀ n {m} -> Vec ℕ (n + m) -> Vec ℕ (m + n)

    test-fancy₁ : _
    test-fancy₁ = fancy₁ 2 (1 ∷ 2 ∷ 3 ∷ [])

    postulate
      fancy₂ : ∀ {n} m -> Vec ℕ (n + m) -> Vec ℕ (m + n)

    test-fancy₂ : Vec ℕ 3
    test-fancy₂ = fancy₂ 2 (1 ∷ 2 ∷ 3 ∷ [])

The second case works, because Agda looks at the type of `test-fancy₂'
and solves (m = 2, m + n = 3) => (n = 1).

A counter-example:

    postulate
      fancy₃ : ∀ {n} m -> Vec ℕ (n + m) -> ⊤

    fail-fancy₃ : ⊤
    fail-fancy₃ = fancy₃ 2 (1 ∷ 2 ∷ 3 ∷ [])

Agda can't infer `n' from (n + m), if `m' is known.

A little more complicated example:

    postulate
      fancy₄ : ∀ n {m p} -> Vec ℕ (n + m + p) -> Vec ℕ (n + m) -> ⊤

    test-fancy₄ : ⊤
    test-fancy₄ = fancy₄ 2 (1 ∷ 2 ∷ 3 ∷ 4 ∷ 5 ∷ []) (1 ∷ 2 ∷ 3 ∷ [])

(n = 2, n + m + p = 5, n + m = 3) => (n = 2, m = 1, p = 2)



  1.4) `f' is constructor-headed [1]. For example `Data.Product.N-ary' defines

    _^_ : ∀ {ℓ} → Set ℓ → ℕ → Set ℓ
    A ^ 0           = Lift ⊤
    A ^ 1           = A
    A ^ suc (suc n) = A × A ^ suc n

It's not constructor-headed: if (A ^ n = ℕ × ℕ) you get two solutions:
  1) A = ℕ     , n = 2
  2) A = ℕ × ℕ , n = 1

But

    _^_ : ∀ {ℓ} → Set ℓ → ℕ → Set ℓ
    A ^ 0       = Lift ⊤
    A ^ (suc n) = A × A ^ suc n

is constructor-headed, and Agda can infer both `A' and `n' from (A ^ n
= B), if `B' is known.

You can make the first definition constructor-headed by wrapping `A'
in the (A ^ 1) case in an additional datatype:

    record Wrap {α} (A : Set α) : Set α where
      constructor wrap
      field el : A

    _^_ : ∀ {ℓ} → Set ℓ → ℕ → Set ℓ
    A ^ 0           = Lift ⊤
    A ^ 1           = Wrap A
    A ^ suc (suc n) = A × A ^ suc n

    test-wrap : _ ^ _
    test-wrap = 0 , 3 , 5 , wrap 9

2) Agda usually can't infer `f' from (f x)

    fun : {L : Set -> Set} -> L ℕ -> ⊤
    fun _ = _

    fail-fun : ⊤
    fail-fun = fun []

results in unresolved metas. But you can help she by limiting the
scope of search using instance arguments:

    record IsNatList (L : Set -> Set) : Set where
      field f : ⊤

    instance
      inst : IsNatList List
      inst = record { f = _ }

    fun-inst : {L : Set -> Set} {{_ : IsNatList L}} -> L ℕ -> ⊤
    fun-inst _ = _

    test-fun-inst : ⊤
    test-fun-inst = fun-inst []

No unresolved metas. Strangely this doesn't work if I omit the
pointless field in the `IsNatList' datatype.

3) Agda's definitional equality is beta-eta, so (proj₁ p , proj₂ p)
reduces to just `p' during typechecking. Using eta-rules Agda can
infer elements of records:

    auto-records : ⊤ × Lift {ℓ = Le.suc (Le.suc Le.zero)} ⊤
    auto-records = _

4) Sometimes Agda can infer more stuff in mutual blocks, but it's
black magic to me.

5) Have a look at [2] for more complicated cases.

[1] http://wiki.portal.chalmers.se/agda/pmwiki.php?n=ReferenceManual.FindingTheValuesOfImplicitArguments
[2] http://www2.tcs.ifi.lmu.de/~abel/talkTLCA11.pdf


More information about the Agda mailing list