Background: I'm working through Prabakhar Ragde's "Logic and Computation Intertwined", a totally fantastic introduction to computational intuitionistic logic. In his last chapter, he walks through some basics using Agda. I've successfully installed Agda and managed to twist emacs' arm (it took a lot of arm-twisting!) to get agda-mode working fairly well, but I feel that I'm missing a summary of the various type ascription forms in Agda.
Specifically, working through a proof in Agda without my head exploding requires a fair amount of type ascription--Let's just say this has this type for now, okay?--and I find myself missing the ability to associate types with names in a uniform way. I now know about two ways of doing this, but I feel as though I'm missing a more general one.
The following illustrates both of these (using definitions of or
and negation that are simple but not included, sorry):
ex9 : ∀ (A B : Set) → ¬ (¬ (or A (¬ A)))
ex9 A B aornotaimpliesbottom = aornotaimpliesbottom (or-intro-2 nota)
where nota : ¬ (A)
nota = λ (a : A) → aornotaimpliesbottom (or-intro-1 a)
Method 1 is used to specify the type of nota
, and method 2 is used to specify the type of nota
's argument.
Here's the question, though: what if I want to use type ascription in an "expression" position? For instance, what if I wanted to specify the type of the term (or-intro-1 a)
? I could use a "where" clause to pull it out into its own binding, but .... ooh, actually, that doesn't seem to work; embedding a 'where' into a lambda is not working as expected. Oh! It looks like "let" works there. Okay, anyway, question remains: is there a lighter-weight way to specify the type of an expression inline?
You can define inline type annotation as follows:
infixl 0 _∋_
_∋_ : ∀{i}(A : Set i) → A → A
A ∋ x = x
Or import it from the standard library:
open import Function using (_∋_)
Then A ∋ exp
works as an inline type annotation on any expression. For example, in your code as:
nota = λ (a : A) → aornotaimpliesbottom (or A (¬ A) ∋ or-intro-1 a)
You can also insert inline type annotation, or effectively query the type of an expression which is already in your code, by first inserting a hole like this:
nota = λ (a : A) → aornotaimpliesbottom (? ∋ or-intro-1 a)
Then hitting C-c-s
while in the hole, which fills the hole using default inference.
It's also good to remember that let
is syntactically more flexible than where
. You can put let
inside any expression, but where
only works at binding scopes (at top level, immediately under module declarations, or immediately in function right hand sides).