Let's start with the postulate. The syntax is simply:
postulate name : type
This asserts that there exists some value of type type
called name
. Think of it as axioms in logic - things that are defined to be true and are not be questioned (by Agda, in this case).
Next up is the data definition. There's a slight oversight with the mixfix declaration so I'll fix it and explain what it does. The first line:
data _∨_ (A B : Set) : Set where
Introduces a new type (constructor) called _∨_
. _∨_
takes two arguments of type Set
and then returns a Set
.
I'll compare it with Haskell. The A
and B
are more or less equivalent to a
and b
in the following example:
data Or a b = Inl a | Inr b
This means that the data definition defines a polymorphic type (a template or a generic, if you will). Set
is the Agda equivalent of Haskell's *
.
What's up with the underscores? Agda allows you to define arbitrary operators (prefix, postfix, infix... usually just called by a single name - mixfix). The underscores just tell Agda where the arguments are. This is best seen with prefix/postfix operators:
-_ : Integer → Integer -- unary minus
- n = 0 - n
_++ : Integer → Integer -- postfix increment
x ++ = x + 1
You can even create crazy operators such as:
if_then_else_ : ...
Next part is definition of the data constructors itself. If you've seen Haskell's GADTs, this is more or less the same thing. If you haven't:
When you define a constructor in Haskell, say Inr
above, you just specify the type of the arguments and Haskell figures out the type of the whole thing, that is Inr :: b -> Or a b
. When you write GADTs or define data types in Agda, you need to specify the whole type (and there are good reasons for this, but I won't get into that now).
So, the data definition specifies two constructors, inl
of type A → A ∨ B
and inr
of type B → A ∨ B
.
Now comes the fun part: first line of classical-2
is a simple type declaration. What's up with the Set
thing? When you write polymorphic functions in Haskell, you just use lower case letters to represent type variables, say:
id :: a -> a
What you really mean is:
id :: forall a. a -> a
And what you really mean is:
id :: forall (a :: *). a -> a
I.e. it's not just any kind of a
, but that a
is a type. Agda makes you do this extra step and declare this quantification explicitly (that's because you can quantify over more things than just types).
And the curly braces? Let me use the Haskell example above again. When you use the id
function somewhere, say id 5
, you don't need to specify that a = Integer
.
If you used normal paretheses, you'd have to provide the actual type A
everytime you called classical-2
. However, most of the time, the type can be deduced from the context (much like the id 5
example above), so for those cases, you can "hide" the argument. Agda then tries to fill that in automatically - and if it cannot, it complains.
And for the last line: λ x → y
is the Agda way of saying \x -> y
. That should explain most of the line, the only thing that remains are the curly braces yet again. I'm fairly sure that you can omit them here, but anyways: hidden arguments do what they say - they hide. So when you define a function from {A}
to B
, you just provide something of type B
(because {A}
is hidden). In some cases, you need to know the value of the hidden argument and that's what this special kind of lambda does: λ {A} →
allows you to access the hidden A!