-- if curious: https://agda.readthedocs.io/en/v2.6.0.1/language/without-k.html
{-# OPTIONS --without-K --allow-unsolved-metas #-}

{-
  CS 598 TLR
  Artifact 1: Proof Objects
  Student Copy

  READ ME FIRST: You will absolutely not be graded on your ability to finish
  these proofs. It's OK to be confused and find this hard. It's also
  OK to come into this class as an Agda wizard and breeze through this,
  perhaps even using a bunch of features of Agda that aren't mentioned
  in this file at all (if you do that, though, please help your partner
  understand what you are doing, and please discuss what you did at the end).
  The point is to pay attention to what is hard and where you wish you
  had better automation, and to learn about proof objects.

  At the end of this class (or, if you're not attending in person,
  sometime before 1230 PM the day of class) you'll post on the forum
  about what you found challenging, what you enjoyed, and─most importantly
  for this class─what kind of automation you wish you had.
  If you or someone you're working with is an Agda wizard already,
  and you know about automation or syntax that already exists that makes the
  job easy, definitely take note of that and mention it as well.

  If you have a lot of trouble with a proof, it's OK to ask me for help
  (or post in the forum if you're not here in person). It's also OK to
  ask people in other groups.

  But please, please don't stress about finishing these proofs.
  The theorems will still be here after class if you feel tempted by them.
  I'm also happy to distribute my own answer key, or to let you share
  proofs with other students later─the point is to think about the proof
  objects you construct throughout, and the experience you have constructing
  them. It's about the journey, not the destination.
-}
module proof-objects where

{-
  Some built-in datatypes we'll use in this exercise:
-}
open import Agda.Builtin.Nat -- natural numbers
open import Agda.Builtin.List -- polymorphic lists
open import Agda.Builtin.Char -- characters
open import Agda.Builtin.Equality -- equality

{-
  You can see these definitions by clicking them and then pressing the Alt
  key along with the period. Or you can use the middle button of your mouse,
  if you have one.

  For example, if we click on Agda.Builtin.List and press the Alt key
  along with the period, it will open up the file that defines the
  list datatype. A polymorphic list in Agda is defined a lot like the
  list datatype in Coq that we saw in the slides for the first class.
  It is an inductive datatype with two cases:

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

  In other words, as in Coq, a list is either empty ([]) or the result
  of consing (∷) an element of type A in front of some other list of
  elements of type A.

  For example, let's define some empty lists of numbers and characters.
  In Agda, a definition has its type on the first line, and then the
  term with that type on the next line:
-}

-- The empty list of natural numbers
empty-nat : List Nat -- the type
empty-nat = [] -- the term

-- The empty list of characters
empty-char : List Char -- the type
empty-char = [] -- the term

{-
  Cool. Now let's define some non-empty lists.

  NOTE: The Agda community has an unfortunate obsession with Unicode.
  The syntax for the cons constructor in Coq is two colons, written ::.
  Confusingly, the Agda cons constructor is actually denoted with a
  single special unicode character, written ∷. You can write this
  special character by typing \:: (with two colons), and it will
  magically turn into ∷.

  OK, with that in mind:
-}

-- A list [1; 2; 3; 4]
1-2-3-4 : List Nat -- type
1-2-3-4 = 1  2  3  4  [] -- term

-- A list [x; y; z]
x-y-z : List Char -- type
x-y-z = 'x'  'y'  'z'  [] -- term

{-
  As in Coq, we can write functions about lists, like our length function,
  which we define by pattern matching. The syntax for pattern matching in
  Agda is defining each case on a different line:
-}

-- list length
length :  {A : Set}  List A  Nat -- type
length [] = 0 -- term in the empty case
length (h  tl) = suc (length tl) -- term in the cons case

{-
  That is, the length of the empty list ([]) is zero (0), and the
  length any other list (h ∷ tl) is one plus (suc, for "successor")
  the length of the tail of the list (length tl).

  NOTE: Thanks to the Agda community's unicode obsession, you can write
  the above ∀ by typing \forall, and you can write the above → by typing
  \r or \->. But it's OK to spell them out as forall and ->.

  EXERCISE 1: Write a function that reverses a list.
  You may use the function app I've written for you below,
  which appends two lists.

  NOTE: The {! !} syntax I've written inside of the sketch of rev is a
  hole─you will want to replace this with your implementation of rev for
  each case. These holes change color when you compile files, and numbers
  appear next to them; I explain this a bit more later on.

  NOTE: You can compile this file by running C-c C-l, to just
  invoke the type checker. To produce an executable file,
  you can run C-c C-x C-c and specify the GHC backend, but this isn't necessary
  to just check the proofs you'll write in this file. The first time
  you compile this file, you'll see a bunch of numbered constrants;
  these correspond to the holes I've left in the file for you to fill.
-}

-- list append
app :  {A : Set}  List A  List A  List A -- type
app [] l = l -- term in the empty case
app (h  tl) l = h  app tl l -- term in the cons case 

-- list rev
rev :  {A : Set}  List A  List A -- type
rev [] = {! !} -- your term in the empty case goes here
rev (h  tl) = {! !} -- your term in the cons case goes here

{-
  Let's test your rev function.
  We're going to check that, on the lists we've defined at the top
  of this file, the result of calling rev gives us the reverse lists.

  To do that, we need a way to state what it means for two things to be
  equal. Equality in Agda is written ≡, which is typed as \== using the same
  magic as before, because of the community's unicode obsession:
  
    data _≡_ {a} {A : Set a} (x : A) : A → Set a where
      refl : x ≡ x

  Equality is an inductive datatype, just like lists. But one thing that's
  cool about the equality type is that it's something called a dependent type.
  A dependent type is a lot like a polymorphic type, but instead of taking
  a type argument (like the A in List A), it takes a _term_ argument
  (like the x in x ≡ x). Dependent types can refer to values!
  So we can define the type of all terms that prove our lists
  behave the way we want them to. And then we can write terms that
  have those types─our proofs.

  There is exactly one constructor for the equality type in Agda:
  refl, for reflexivity. Two things are equal by reflexivity when they
  compute to the same result. So if your rev function is correct,
  these test cases should compile:
-}

-- empty-nat doesn't change
rev-empty-nat-empty-nat : rev empty-nat  empty-nat
rev-empty-nat-empty-nat = refl

-- empty-char doesn't change
rev-empty-char-empty-char : rev empty-char  empty-char
rev-empty-char-empty-char = refl

-- [1; 2; 3; 4] becomes [4; 3; 2; 1]
rev-1-2-3-4-4-3-2-1 : rev 1-2-3-4  4  3  2  1  []
rev-1-2-3-4-4-3-2-1 = refl

-- [x; y; z] becomes [z; y; x]
rev-x-y-z-z-y-x : rev x-y-z  'z'  'y'  'x'  []
rev-x-y-z-z-y-x = refl

{-
  These test cases are actually proofs! Even though they all hold
  by computation (reflexivity), they are terms, just like the lists
  we refer to inside of them. Their types are the theorems that they prove.
  
  That's the beauty of Curry-Howard; our terms here are proof objects
  that prove the theorems stated by the types they inhabit.
  These programs are proofs!

  So now it's your turn to test my length function and, in the process,
  write some proofs yourself.

  EXERCISE 2: Prove that 1-2-3-4 and x-y-z have the right lengths.
  As before, you'll want to replace each hole {! !} with your own code.
  This time, I want you to define both the types and the terms.
-}

-- 1-2-3-4 has the right length
length-1-2-3-4-OK : {! !}
length-1-2-3-4-OK = {! !}

-- x-y-z has the right length
length-x-y-z-OK : {! !}
length-x-y-z-OK = {! !}

{-
  Those proofs are very small proofs that hold by computation.
  There are also some cooler proofs we can prove by computation,
  like that appending the empty list to the left of any list
  gives us back that list:
-}

-- left identity of nil for append
app-nil-l :  {A : Set}  (l : List A)  app [] l  l -- type or theorem
app-nil-l l = refl -- term or proof

{-
  But sometimes we need to get fancier! For example, if we simply swap
  the [] and l in the type definition above above, to define app-nil-r,
  this will no longer hold by reflexivity, since app is defined by
  matching over the first list, not the second. Because of this,
  when you put the empty list as the first argument, Agda can reduce it;
  when you put it as the second argument instead, Agda cannot reduce it.
  
  If we try to prove app-nil-r by reflexivity, we'll get a scary looking
  type error, like this:

    app l [] != l of type List A
    when checking that the expression refl has type app l [] ≡ l

  But here's the thing─the equality is still true!
  It's just not true by computation. We have to prove it differently.
  Agda needs our help figuring this out.

  So let's help Agda. We're going to need to write some fancier proofs
  about equality. But how do we do that if there's just one constructor
  for equality─refl? Well, there are two things we can do with inductive
  datatypes: we can construct them, and we can pattern match over their cases
  (the latter is also called destructing them or, for reasons you'll see later,
  in specific contexts, inducting over them).
  
  Since equality is defined as an inductive type, we can pattern match
  over it just like we do over lists. When we do, there will be just one
  case─refl. But that actually turns out to give us a lot of power to
  prove some cool things about equality. So let's do that.

  Our first lemma is congruence, which comes from the standard library,
  and states that function application preserves equality:
-}

-- congruence
cong :  {A B : Set} (f : A  B) {x y : A}  x  y  f x  f y -- the lemma
cong f refl = refl -- the proof

{-
  In other words, this matches over the input argument of type x ≡ y.
  Since equality has just one case, pattern matching breaks this into
  just one case, which is when that equality is refl.
  When that equality is refl, then we have x ≡ x.
  Thus, we are trying to show f x ≡ f x, which also holds by refl,
  since these terms are the same.

  One thing to note here is that it can be super hard to track this information
  in your head. In Agda, you can create holes in your functions
  by typing {! !}, much like I've done for you throughout. So you could write:

    cong f refl = {! !}

  If you compile that with C-c C-l, it'll change colors and there
  will be a 0 next to it, indicating that it's the 0th goal. In the AgdaInfo
  buffer, you'll see the goal that you need to satisfy for that hole:

    ?0 : f x ≡ f x

  This goal is the type you're trying to inhabit. From there,
  you can figure out that refl has that type, without having to track
  so much information in your head about what Agda is doing.

  With this, we should be able to prove app-nil-r:
-}

-- right identity of nil for append
app-nil-r :  {A : Set}  (l : List A)  app l []  l
app-nil-r [] = refl
app-nil-r (h  tl) = cong  l′  h  l′) (app-nil-r tl)

{-
  EXERCISE 3: Prove the lemma sym, which states that equality is symmetric.
  I have inserted a hole into this lemma, denoted {! !}, which may
  already look green if you've compiled this file. You can use the
  workflow described above to figure out the type you're trying to inhabit.
-}

-- symmetry of equality
sym :  {A : Set} {x y : A}  x  y  y  x
sym refl = {! !}

{-
  EXERCISE 4: Prove that l ≡ app l [], using symmetry. I have inserted
  a hole into this lemma where you will need to figure out a term.
-}

-- symmetric right identity of nil for append
app-nil-r-sym :  {A : Set}  (l : List A)  l  app l []
app-nil-r-sym l = sym {! !}

{-
  EXERCISE 5: Prove the lemma trans, which states that equality
  is transitive. I have inserted a hole into this lemma as well.
-}

-- transitivity of equality
trans :  {A : Set} {x y z : A}  x  y  y  z  x  z
trans refl refl = {! !}

{-
  Equalities like this that we have to prove by pattern matching over
  the equality type are what we called propositional equalities
  in QED at Large. This is in contrast with definitional equalities,
  which hold by computation.

  For example, app [] l and l are definitionally equal, since Agda
  reduces both of them to the same term. But app l [] and l are not
  definitionally equal, as we saw earlier, since app is defined by
  pattern matching over the first term rather than the second.
  They are only propositionally─provably, that is─equal.

  In Agda, Coq, and Lean, two things that are definitionally equal are
  necessarily propositionally equal, but the reverse is not necessarily true.
  This is why we can't prove app-nil-r by reflexivity.

  This is important to pay attention to─in many of these languages, it is
  really standard for your proofs to mirror the structure of
  the programs they are about. There is a deep type theoretical
  reason that this is true.

  ASIDE: Definitional equality is─for many proof assistants we care about in
  this class─a decidable judgment that follows from a few reduction rules,
  like unfolding some constants (δ-expansion), applying functions to
  arguments (Β-reduction, which is sometimes called ι-reduction for
  inductive datatypes), renaming (α-conversion), and more.

  These may sound familiar if you're familiar with the λ-calculus,
  a simple functional programming language that is often used as an example
  in programming languages courses. The proof assistants Agda, Coq, and Lean
  all build on extensions of the λ-calculus with not just simple types,
  but also polymorphism (like how list takes a type argument A), dependent
  types (like how ≡ takes a term argument x), and inductive types (like
  how both the list and ≡ datatypes are defined by defining all ways to
  construct them).
  
  But if you haven't heard of these, that's OK. I super recommend reading this
  chapter of Certified Programming with Dependent Types by Adam Chlipala:
  http://adam.chlipala.net/cpdt/html/Equality.html. If you'd like more
  information, that will really help─though it's in Coq. I hope to
  show you some automation that steps through equalities like this next week.

  BACK TO BUSINESS: It can be kind of annoying to keep pattern matching
  over equality, which is why the lemmas above─also defined in the standard
  library─can be really useful.

  An especially useful lemma about equality is substitution,
  which is also found inside of the standard library:
-}

-- substitution
subst :  {A : Set} {x y : A} (P : A  Set)  x  y  P x  P y
subst P refl px = px

{-
  This states that for any property P, if x ≡ y, and we have a proof
  that P x holds, we can substitute y for x inside of that to show
  that P y holds. This is really powerful, but a little hard to use,
  because you have to figure out what P is. For example, here is a proof
  of app-nil-r using subst instead of cong:
-}

-- right identity of nil for append, proven by substitution instead
app-nil-r′ :  {A : Set}  (l : List A)  app l []  l
app-nil-r′ [] = refl
app-nil-r′ (h  tl) = subst  l′  h  l′  h  tl) (sym (app-nil-r′ tl)) refl

{-
  Your turn to try. You can use cong or subst here─whatever you find
  more convenient.

  EXERCISE 6: Finish the proof of length-succ-r.
-}
length-succ-r :
   {A}  (l : List A)  (a : A)  length (app l (a  []))  suc (length l)
length-succ-r [] a = refl  -- base case
length-succ-r {A} (h  tl) a = -- inductive case
  {! !}

{-
  EXERCISE 7: Show that the reverse function that you wrote preserves
  the length of the input list. You may use length-succ-r, and any of the
  equality lemmas we've defined so far like trans, subst, sym, and
  cong. (You probably won't need all of them.)
-}
rev-pres-length :  {A}  (l : List A)  length (rev l)  length l
rev-pres-length [] = {! !}
rev-pres-length (h  tl) = {! !}

{-
  That's it for now! You can keep playing with other proofs if you have
  extra time. If you have a lot of extra time, I recommend looking at the
  Agda source code on Github to get a sense for how it's implemented:
  https://github.com/agda/agda. In any case, when we have 25 minutes
  left of class, please do this:

    1. Turn to your group and discuss the question below.
    2. Post your answer─just one answer for your group, clearly indicating
       all members of the group. (If you are not here, and are working alone,
       you can post your answer alone.)
    3. With 10 minutes left, finish posting your answer, so we can discuss
       a bit as a class.

  You'll be graded based on whether you post an answer, not based on
  what it is, so don't worry too much about saying something silly.

  DISCUSSION QUESTION: What was it like constructing these proofs directly,
  as terms in a programming language? What did you find challenging about
  this experience, if anything? What did you find helpful, if anything? Did
  you get stuck at any point, and if so, where and why? Where do you wish
  you'd had more automation to help you out?
-}