{- 
  Computer Aided Formal Reasoning (G53CFR, G54CFR)
  Thorsten Altenkirch

  Lecture 2: A first taste of Agda

  In this lecture we start to explore the Agda system, a functional
  programming language based on Type Theory. We start with some
  ordinary examples which we could have developed in Haskell as well. 
-}

module l02 where

-- module myNat where

  {- Agda has no automatically loaded prelude. Hence we can start from
     scratch and define the natural numbers. Later we will use the
     standard libray. -}
  
  data ℕ : Set where -- to type ℕ we type \bn 
    zero : ℕ
    suc : (m : ℕ) → ℕ -- \-> or \to

  {- To process an Agda file we use C-c C-c from emacs. Once Agda has
     checked the file the type checker also colours the different
     symbols. -}

  {- We define addition. Note Agda's syntax for mixfix operations. The
     arguments are represented by _s -}

  _+_ : ℕ → ℕ → ℕ
  zero + n = n
  suc m + n = suc (m + n)

  {- Try to evaluate: (suc (suc zero)) + (suc (suc zero))
     by typing in C-c C-n -}

{- Better we import the library definition of ℕ
   This way we can type 2 instead of (suc (suc zero))
-}
  
-- open import Data.Nat

{- We define Lists :  -}

data List (A : Set) : Set where
  [] : List A
  _∷_ : (a : A) → (as : List A) → List A

{- In future we'll use 
     open import Data.List -}

{- declare the fixity of ∷ (type \::) -}

infixr 5 _∷_

{- Two example lists -}

{-
l1 : List ℕ
l1 = 1 ∷ 2 ∷ 3 ∷ []

l2 : List ℕ
l2 = 4 ∷ 5 ∷ []
-}

{- implementing append (++) -}

_++_ : {A : Set} → List A → List A → List A
[] ++ bs = bs
(a ∷ as) ++ bs = a ∷ (as ++ bs)

{- Note that Agda checks wether a function is terminating.
   If we type
   (a ∷ as) ++ bs = (a ∷ as) ++ bs
   in the 2nd line Agda will complain by coloring the offending 
   function calls in red.
-}

{- What does the following variant of ++ do ? -}

_++'_ : {A : Set} → List A → List A → List A
as ++' [] = as
as ++' (b ∷ bs) = (b ∷ as) ++' bs

{- Indeed it can be used to define reverse.  This way to implement
   reverse is often called fast reverse because it is "tail recursive"
   which leads to a more efficient execution than the naive
   implementation. -}

rev : {A : Set} → List A → List A
rev as = [] ++' as

{- We tried to define a function which accesses the nth element of a list:

_!!_ : {A : Set} → List A → ℕ → A
[] !! n = {!!}
(a ∷ as) !! zero = a
(a ∷ as) !! suc n = as !! n

  but there is no way to complete the first line (consider what happens
  if A is the empty type!  
-}

{- To fix this we handle errors explicitely, using Maybe -}

-- open import Data.Maybe

data Maybe (A : Set) : Set where
  just    : (x : A) → Maybe A
  nothing : Maybe A

{- This version of the function can either return an element of the list 
    (just a) or an error (nothing).
-}
_!!_ : {A : Set} → List A → ℕ → Maybe A
[] !! n = nothing
(a ∷ as) !! zero = just a
(a ∷ as) !! suc n = as !! n
