🌍 Global Mirror β€” Visit original CN site β†’

Learn X in Y minutes

Where X=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 })

Further Reading


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.