Get the code: learnagda.agda
Agda is a dependently typed functional programming language. It is an extension of Martin-LΓΆf's Type Theory. In Agda, programs and proofs are written in the same language. In Agda, there is no separation between types and values: types can depend on values, and values can act as types. This allows you to encode logical invariants (like the length of a vector) directly into the type system, making Agda a powerful tool for both programming and theorem proving.
Agda is heavily reliant on Unicode characters. Most editors (like Emacs or VS Code) support entering these via LaTeX-like abbreviations (e.g., \to for β, \all for β, \bN for β).
To install Agda, you typically use Haskell's cabal or stack. See the wiki for details.
-- A module must have the same name as the file (excluding .agda)
module learnagda where
-- We can import other modules.
-- Agda's standard library is commonly used, but here we will define
-- the basics from scratch for educational purposes.
-- Agda is a dependently typed functional programming language.
-- It is strictly total:
-- all programs must terminate, and all patterns must be matched.
-- Agda logic relies heavily on the Curry-Howard correspondence:
-- Propositions are Types
-- Proofs are Programs
-- Comments use double dashes for single lines.
{- Multi-line comments are enclosed in braces and hyphens. -}
-- Agda uses Unicode extensively.
-- In the Emacs mode (or VS Code), you type these using LaTeX-like shortcuts:
-- \to β
-- \bN β
-- \== β‘
-- \all β
-- \:: β·
--------------------------------------------------------------------------------
-- Datatypes and Pattern Matching
--------------------------------------------------------------------------------
-- 'Set' is the type of types (similar to 'Type' in other languages).
-- We define Booleans as an inductive data type.
data Bool : Set where
true : Bool
false : Bool
-- We can define functions using pattern matching.
-- 'not' takes a Bool and returns a Bool.
not : Bool β Bool -- The type signature is mandatory.
not true = false
not false = true
-- Natural numbers (Peano encoding).
-- This defines an infinite set of terms: zero, suc zero,
-- suc (suc zero), ...
data β : Set where
zero : β
suc : β β β
-- We can tell Agda to treat β as normal numbers for literals
-- by using a pragma.
{-# BUILTIN NATURAL β #-}
-- Addition defined recursively.
-- Underscores denote where the arguments go for infix operators.
-- This is Agda's "Mixfix" notation. You can put underscores anywhere!
_+_ : β β β β β
zero + n = n
suc m + n = suc (m + n)
-- We can define precedence for operators.
infixl 6 _+_
-- Example evaluation:
-- 2 + 3 = 5
-- To verify this in an editor:
-- Type "2 + 3", select it, and press C-c C-n (Compute Normal Form).
-- Mixfix examples:
-- if_then_else_ : Bool β A β A β A
-- if true then x else y
--------------------------------------------------------------------------------
-- Interaction and Holes
--------------------------------------------------------------------------------
-- Before introducing other concepts, we need to introduce 'holes'.
-- Agda development is interactive.
-- You write types, and the system helps fill terms.
-- A question mark ? or {! !} creates a "hole".
-- example : 2 + 2 β‘ 4
-- example = ?
-- While in Emacs/VS Code:
-- C-c C-l : Load file (type checks).
-- C-c C-space : Given a hole, ask Agda to fill it (Auto).
-- C-c C-r : Refine. If the hole is for a data type,
-- splits constructors.
-- C-c C-, : Goal type and context. Tells you what you need to prove.
-- POP QUIZ: Define multiplication for natural numbers.
-- Hint: split cases, and then do induction.
_*_ : β β β β β
x * y = ?
--------------------------------------------------------------------------------
-- Polymorphism and Variables
--------------------------------------------------------------------------------
-- In modern Agda, we can declare generalizable variables to avoid
-- repetitive `forall` syntax in type signatures.
variable
A B : Set
n m : β
-- Lists are parameterized by a type A.
infixr 5 _::_
data List (A : Set) : Set where
[] : List A -- Empty list
_::_ : A β List A β List A -- Cons constructor
-- A list of numbers: 1 :: 2 :: 3 :: []
nums : List β
nums = 1 :: 2 :: 3 :: []
-- Map function showing explicit universe polymorphism (optional)
-- Here we use implicit arguments (denoted by { }).
-- Agda infers them from context.
-- β {A B} makes A and B implicit type variables.
map : β {A B : Set} β (A β B) β List A β List B
map f [] = []
map f (x :: xs) = f x :: map f xs
-- Anonymous functions (lambdas)
-- We can write `Ξ» x β x + 1`
plus1 : β β β
plus1 = Ξ» x β x + 1
--------------------------------------------------------------------------------
-- Dependent Types
--------------------------------------------------------------------------------
-- Dependent types allow types to depend on values.
-- A classic example is a Vector: a list with a fixed length
-- encoded in its type.
data Vec (A : Set) : β β Set where
[] : Vec A zero
_::_ : β {n} β A β Vec A n β Vec A (suc n)
-- If we try to create a vector with the wrong length, it's a type error.
vec3 : Vec β 3
vec3 = 1 :: 2 :: 3 :: []
-- Because the length is known, we can define 'safe' operations.
-- This function cannot be called on an empty vector.
head : Vec A (suc n) β A
head (x :: xs) = x
-- We can calculate the exact type of a concatenation.
_++_ : Vec A n β Vec A m β Vec A (n + m)
[] ++ ys = ys
(x :: xs) ++ ys = x :: (xs ++ ys)
--------------------------------------------------------------------------------
-- Equality and Proofs
--------------------------------------------------------------------------------
-- The identity type (equality) is the heart of proving in Agda.
-- x β‘ y is a type that has a value only if x and y are the same normal form.
infix 4 _β‘_
data _β‘_ {A : Set} (x : A) : A β Set where
refl : x β‘ x
{-# BUILTIN EQUALITY _β‘_ #-}
-- PROOFS ARE PROGRAMS
-- To prove a property, we write a function that produces a value of that type.
-- Proving 1 + 1 equals 2.
1+1β‘2 : 1 + 1 β‘ 2
1+1β‘2 = refl
-- Congruence: applying a function to equal values yields equal results.
cong : β {A B : Set} (f : A β B) {x y : A}
β x β‘ y β f x β‘ f y
cong f refl = refl
-- Symmetry
sym : β {A : Set} {x y : A} β x β‘ y β y β‘ x
sym refl = refl
-- Transitivity
trans : β {A : Set} {x y z : A} β x β‘ y β y β‘ z β x β‘ z
trans refl refl = refl
-- Proving associativity of addition.
-- This requires induction on x.
-- We split cases on x.
-- If x is zero: zero + (y + z) β‘ zero + y + z
-- reduces to y + z β‘ y + z.
-- If x is suc x: we use the inductive hypothesis (recursion).
+-assoc : β (x y z : β) β (x + y) + z β‘ x + (y + z)
+-assoc zero y z = refl
+-assoc (suc x) y z =
let
-- The inductive hypothesis
ih = +-assoc x y z
in
-- We need to prove: suc (x + y) + z β‘ suc (x + (y + z))
-- Using congruence on the IH gives us exactly that.
cong suc ih
-- Agda provides a 'rewrite' keyword to simplify this.
-- It pattern matches on the equality proof effectively replacing x with y.
+-identity : (n : β) β n + zero β‘ n
+-identity zero = refl
+-identity (suc n) rewrite +-identity n = refl
--------------------------------------------------------------------------------
-- Equational Reasoning
--------------------------------------------------------------------------------
-- Agda allows defining custom syntax to write proofs
-- that look like calculations.
-- This is similar to Lean's `calc` mode but defined within the language.
module Reasoning {A : Set} where
infix 1 begin_
infixr 2 _β‘β¨β©_ _β‘β¨_β©_
infix 3 _β
begin_ : β {x y : A} β x β‘ y β x β‘ y
begin p = p
_β‘β¨β©_ : β (x : A) {y : A} β x β‘ y β x β‘ y
x β‘β¨β© p = p
_β‘β¨_β©_ : β (x : A) {y z : A} β x β‘ y β y β‘ z β x β‘ z
x β‘β¨ xβ‘y β© yβ‘z = trans xβ‘y yβ‘z
_β : β (x : A) β x β‘ x
x β = refl
open Reasoning
-- Proof of associativity using reasoning syntax
+-assoc' : β (x y z : β) β (x + y) + z β‘ x + (y + z)
+-assoc' zero y z = refl
+-assoc' (suc x) y z =
begin
(suc x + y) + z
β‘β¨β© -- Definition of +
suc (x + y) + z
β‘β¨β© -- Definition of +
suc ((x + y) + z)
β‘β¨ cong suc (+-assoc' x y z) β©
suc (x + (y + z))
β‘β¨β©
suc x + (y + z)
β
--------------------------------------------------------------------------------
-- Inductive Relations
--------------------------------------------------------------------------------
-- Relations can also be defined as inductive types.
-- Here we define "less than or equal to" for natural numbers.
data _β€_ : β β β β Set where
zβ€n : β {n} β zero β€ n
sβ€s : β {m n} β m β€ n β suc m β€ suc n
-- Proof that 2 β€ 4
2β€4 : 2 β€ 4
2β€4 = sβ€s (sβ€s zβ€n)
--------------------------------------------------------------------------------
-- Mutual Recursion: Even and Odd
--------------------------------------------------------------------------------
-- We can define types that depend on each other using `mutual`.
mutual
data Even : β β Set where
zero : Even zero
suc : β {n} β Odd n β Even (suc n)
data Odd : β β Set where
suc : β {n} β Even n β Odd (suc n)
mutual
e+eβ‘e : β {m n} β Even m β Even n β Even (m + n)
e+eβ‘e zero en = en
e+eβ‘e (suc om) en = suc (o+eβ‘o om en)
o+eβ‘o : β {m n} β Odd m β Even n β Odd (m + n)
o+eβ‘o (suc em) en = suc (e+eβ‘e em en)
--------------------------------------------------------------------------------
-- Records and Type Classes
--------------------------------------------------------------------------------
-- Records are similar to structs.
record Isomorphism (A B : Set) : Set where
field
to : A β B
from : B β A
fromβto : β (x : A) β from (to x) β‘ x
toβfrom : β (y : B) β to (from y) β‘ y
-- Agda supports "Instance Arguments" via {{ }}.
-- This works like Type Classes in Haskell or Lean.
record Monoid (A : Set) : Set where
field
mempty : A
_<>_ : A β A β A
-- A function using the type class
concat : β {A : Set} {{m : Monoid A}} β List A β A
concat {{m}} [] = Monoid.mempty m
concat {{m}} (x :: xs) = Monoid._<>_ m x (concat {{m}} xs)
-- Instances are defined like normal values but marked 'instance'
instance
natPlusMonoid : Monoid β
natPlusMonoid = record { mempty = 0 ; _<>_ = _+_ }
-- Now we can use it implicitly
sumList : β
sumList = concat (1 :: 2 :: 3 :: []) -- Result: 6
--------------------------------------------------------------------------------
-- Propositions as Types
--------------------------------------------------------------------------------
data _Γ_ (A B : Set) : Set where -- AND
_,_ : A β B β A Γ B
data _β_ (A B : Set) : Set where -- OR
injβ : A β A β B
injβ : B β A β B
data β₯ : Set where -- FALSE
-- No constructors
-- Ex Falso Quodlibet: From falsehood, anything follows.
-- We use an absurd pattern () to indicate this case is impossible.
β₯-elim : β {A : Set} β β₯ β A
β₯-elim ()
-- Negation is defined as "implies False".
Β¬_ : Set β Set
Β¬ A = A β β₯
-- Example: Proving 1 is not equal to 0.
-- We assume 1 β‘ 0 is true (argument eq), and show it leads to
-- a contradiction.
1β’0 : Β¬ (1 β‘ 0)
1β’0 ()
-- The pattern () automatically realizes that 1 β‘ 0 is an impossible match
-- because the constructors 'suc' and 'zero' do not clash
-- in the definition of β‘.
-- Decidable Propositions
-- A property P is decidable if we can compute whether it holds
-- or not.
data Dec (P : Set) : Set where
yes : P β Dec P
no : (P β β₯) β Dec P
-- Computable equality for Naturals
_==_ : (n m : β) β Dec (n β‘ m)
zero == zero = yes refl
zero == suc m = no Ξ»()
suc n == zero = no Ξ»()
suc n == suc m with n == m
... | yes p = yes (cong suc p)
... | no Β¬p = no (Ξ» { refl β Β¬p refl })
Got a suggestion? A correction, perhaps? Open an Issue on the GitHub Repo, or make a pull request yourself!
Originally contributed by pe200012, and updated by 1 contributor.