---------------------------------------------------------------------------------------------------------------

module A201901.Chapter5 where

open import A201901.Prelude public
  hiding (false ; not ; suc ; true ; zero)

import A201901.Chapter3


---------------------------------------------------------------------------------------------------------------
--
-- 5. The untyped lambda-calculus
--

---------------------------------------------------------------------------------------------------------------
--
-- 5.1. Basics
-- “The syntax of the lambda-calculus comprises just three sorts of terms. …”

record IsLC (Term : Set₀) : Set₀ where
  infixl 7 _$_
  field
    ν    : ∀ (x : Name) → Term
    ƛ_∙_ : ∀ (x : Name) (t : Term) → Term
    _$_  : ∀ (t₁ t₂ : Term) → Term

record LC : Set₁ where
  field
    Term : Set₀
    isLC : IsLC Term

  open IsLC isLC public

  instance
    Term-isString : IsString Term
    Term-isString = record
      { Constraint = λ s → ⊤
      ; fromString = λ s → ν (name s)
      }


---------------------------------------------------------------------------------------------------------------
--
-- • Abstract and concrete syntax
-- • Variables and metavariables
-- • Scope
-- • Operational semantics


---------------------------------------------------------------------------------------------------------------
--
-- 5.2. Programming in the lambda-calculus
--
-- • Multiple arguments
-- • Church booleans

module Church-Part1 (lc : LC)
  where
    open LC lc

    tru  = ƛ "t" ∙ ƛ "f" ∙ "t"
    fls  = ƛ "t" ∙ ƛ "f" ∙ "f"
    test = ƛ "l" ∙ ƛ "m" ∙ ƛ "n" ∙ "l" $ "m" $ "n"
    and  = ƛ "b" ∙ ƛ "c" ∙ "b" $ "c" $ fls


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.1. Exercise [⋆]
-- “Define logical `or` and `not` functions.”

    or  = ƛ "b" ∙ ƛ "c" ∙ "b" $ tru $ "c"
    not = ƛ "b" ∙ "b" $ fls $ tru


---------------------------------------------------------------------------------------------------------------
--
-- • Pairs

    pair = ƛ "f" ∙ ƛ "s" ∙ ƛ "b" ∙ "b" $ "f" $ "s"
    fst  = ƛ "p" ∙ "p" $ tru
    snd  = ƛ "p" ∙ "p" $ fls


---------------------------------------------------------------------------------------------------------------
--
-- • Church numerals

    c₀ = ƛ "s" ∙ ƛ "z" ∙ "z"
    c₁ = ƛ "s" ∙ ƛ "z" ∙ "s" $ "z"
    c₂ = ƛ "s" ∙ ƛ "z" ∙ "s" $ ("s" $ "z")
    c₃ = ƛ "s" ∙ ƛ "z" ∙ "s" $ ("s" $ ("s" $ "z"))

    sc = ƛ "n" ∙ ƛ "s" ∙ ƛ "z" ∙ "s" $ ("n" $ "s" $ "z")


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.2. Exercise [⋆⋆]
-- “Find another way to define the successor function on Church numerals.”

    sc2 = ƛ "n" ∙ ƛ "s" ∙ ƛ "z" ∙ "n" $ "s" $ ("s" $ "z")


---------------------------------------------------------------------------------------------------------------

    plus  = ƛ "m" ∙ ƛ "n" ∙ ƛ "s" ∙ ƛ "z" ∙ "m" $ "s" $ ("n" $ "s" $ "z")
    times = ƛ "m" ∙ ƛ "n" ∙ "m" $ (plus $ "n") $ c₀


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.3. Exercise [⋆⋆]
-- “Is it possible to define multiplication on Church numerals without using `plus`?”

    times2 = ƛ "m" ∙ ƛ "n" ∙ ƛ "s" ∙ ƛ "z" ∙ "m" $ ("n" $ "s") $ "z"
    times3 = ƛ "m" ∙ ƛ "n" ∙ ƛ "s" ∙ "m" $ ("n" $ "s")


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.4. Exercise [Recommended, ⋆⋆]
-- “Define a term for raising one number to the power of another.”

    power = ƛ "n" ∙ ƛ "k" ∙ "k" $ (times $ "n") $ c₁


---------------------------------------------------------------------------------------------------------------

    iszro = ƛ "m" ∙ "m" $ (ƛ "x" ∙ fls) $ tru

    zz  = pair $ c₀ $ c₀
    ss  = ƛ "p" ∙ pair $ (snd $ "p") $ (plus $ c₁ $ (snd $ "p"))
    prd = ƛ "m" ∙ fst $ ("m" $ ss $ zz)


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.5. Exercise [⋆⋆]
-- “Use `prd` to define a subtraction function.”

    minus = ƛ "m" ∙ ƛ "n" ∙ "n" $ prd $ "m"


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.6. Exercise [⋆⋆]
-- “Approximately how many steps of evaluation (as a function of `n`) are required to calculate `prd cₙ`?”
-- (skipped)


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.7. Exercise [⋆⋆]
-- “Write a function `equal` that tests two numbers for equality and returns a Church boolean.”

    equal = ƛ "m" ∙ ƛ "n" ∙ and $ (iszro $ ("m" $ prd $ "n"))
                                $ (iszro $ ("n" $ prd $ "m"))


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.8. Exercise [Recommended, ⋆⋆⋆]
-- “A list can be represented in the lambda-calculus by its `fold` function. … What would the representation
-- of `nil` be?  Write a function `cons` that takes an element `h` and a list (that is, a fold function) `t`
-- and returns a similar representation of the list formed by prepending `h` to `t`.  Write `isnil` and `head`
-- functions, each taking a list parameter.  Finally, write a `tail` function for this representation of lists
-- (this is quite a bit harder and requires a trick analogous to the one used to define `prd` for numbers).”

    nil   = ƛ "c" ∙ ƛ "n" ∙ "n"
    cons  = ƛ "h" ∙ ƛ "t" ∙ ƛ "c" ∙ ƛ "n" ∙ "c" $ "h" $ ("t" $ "c" $ "n")
    isnil = ƛ "l" ∙ "l" $ (ƛ "h" ∙ ƛ "t" ∙ fls) $ tru
    head  = ƛ "l" ∙ "l" $ (ƛ "h" ∙ ƛ "t" ∙ "h") $ fls

    tail  = ƛ "l" ∙ fst $ "l" $ (ƛ "x" ∙ ƛ "p" ∙ pair $ (snd $ "p") $ (cons $ "x" $ (snd $ "p"))) $
                                (pair $ nil $ nil)


---------------------------------------------------------------------------------------------------------------
--
-- • Enriching the calculus

record IsLCNB (Term : Set₀) : Set₀ where
  field
    isLC            : IsLC Term
    true false zero : Term
    suc pred iszero : ∀ (t : Term) → Term
    if_then_else    : ∀ (t₁ t₂ t₃ : Term) → Term

  open IsLC isLC public

record LCNB : Set₁ where
  field
    Term   : Set₀
    isLCNB : IsLCNB Term

  open IsLCNB isLCNB public

  lc : LC
  lc = record { isLC = isLC }

  open LC lc public using (Term-isString)


module Church-Part2 (lcnb : LCNB)
  where
    open LCNB lcnb
    open Church-Part1 lc

    realbool   = ƛ "b" ∙ "b" $ true $ false
    churchbool = ƛ "b" ∙ if "b" then tru else fls

    realeq     = ƛ "m" ∙ ƛ "n" ∙ (equal $ "m" $ "n") $ true $ false
    realnat    = ƛ "m" ∙ "m" $ (ƛ "x" ∙ suc "x") $ zero


---------------------------------------------------------------------------------------------------------------
--
-- • Recursion

    omega = (ƛ "x" ∙ "x" $ "x") $ (ƛ "x" ∙ "x" $ "x")

    fix = ƛ "f" ∙ (ƛ "x" ∙ "f" $ (ƛ "y" ∙ "x" $ "x" $ "y")) $
                  (ƛ "x" ∙ "f" $ (ƛ "y" ∙ "x" $ "x" $ "y"))

    f-step = ƛ "f" ∙ ƛ "n" ∙
               if realeq $ "n" $ c₀
               then c₁
               else (times $ "n" $ ("f" $ (prd $ "n")))

    factorial = fix $ f-step


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.9. Exercise [⋆]
-- “Why did we use a primitive `if` in the definition of `g`, instead of the Church-boolean `test` function on
-- Church booleans?  Show how to define the `factorial` function in terms of `test` rather than `if`.

    f-step2 = ƛ "f" ∙ ƛ "n" ∙
                test $ (iszro $ "n") $
                       (ƛ "x" ∙ c₁) $
                       (ƛ "x" ∙ (times $ "n" $ ("f" $ (prd $ "n")))) $ c₀

    factorial2 = fix $ f-step2


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.10. Exercise [⋆⋆]
-- “Define a function `churchnat` that converts a primitive natural number into the corresponding Church
-- numeral.”

    cn-step = ƛ "f" ∙ ƛ "m" ∙
                if iszero "m"
                then c₀
                else sc $ ("f" $ (pred "m"))

    churchnat = fix $ cn-step


---------------------------------------------------------------------------------------------------------------
--
-- 5.2.11. Exercise [Recommended, ⋆⋆]
-- “Use `fix` and the encoding of lists from Exercise 5.2.8 to write a function that sums lists of Church
-- numbers.”

    s-step = ƛ "f" ∙ ƛ "l" ∙
               test $ (isnil $ "l") $
                      (ƛ "x" ∙ c₀) $
                      (ƛ "x" ∙ (plus $ (head $ "l") $ ("f" $ (tail $ "l")))) $ c₀

    sum = fix $ s-step


---------------------------------------------------------------------------------------------------------------
--
-- • Representation


---------------------------------------------------------------------------------------------------------------
--
-- 5.3. Formalities
--
-- • Syntax


---------------------------------------------------------------------------------------------------------------
--
-- 5.3.1. Definition [Terms]
-- “The set of terms is the smallest set `T` such that…”
-- “The _size_ of a term `t` can be defined exactly as we did for arithmetic expressions in Definition 3.3.2.”

open A201901.Prelude using (suc ; zero)

module Functions
  where
    infixl 7 _$_
    data Term : Set₀ where
      ν    : ∀ (x : Name) → Term
      ƛ_∙_ : ∀ (x : Name) (t : Term) → Term
      _$_  : ∀ (t₁ t₂ : Term) → Term

    Functions-lc : LC
    Functions-lc = record
      { Term = Term
      ; isLC = record
        { ν    = ν
        ; ƛ_∙_ = ƛ_∙_
        ; _$_  = _$_
        }
      }

    size : Term → Nat
    size (ν x)     = 1
    size (ƛ x ∙ t) = 1 + size t
    size (t₁ $ t₂) = 1 + (size t₁ + size t₂)

    size-positive : ∀ t → 1 ≤ size t
    size-positive (ν x)     = ≤-refl
    size-positive (ƛ x ∙ t) = s≤s z≤n
    size-positive (t₁ $ t₂) = s≤s z≤n

    infix 5 _<ˢ_
    _<ˢ_ : Rel₀ Term
    _<ˢ_ = _<_ on size

    <ˢ-abs : ∀ x t → t <ˢ ƛ x ∙ t
    <ˢ-abs x t = ≤-refl

    <ˢ-abs′ : ∀ x t t′ → size t ≡ size t′ → t′ <ˢ ƛ x ∙ t
    <ˢ-abs′ x t t′ s≡s′ = begin
        suc (size t′)
      ≡⟨ suc & (s≡s′ ⁻¹) ⟩
        suc (size t)
      ∎

    <ˢ-app₁ : ∀ t₁ t₂ → t₁ <ˢ t₁ $ t₂
    <ˢ-app₁ t₁ t₂ = ≤-step
      (begin
        suc (size t₁)
      ≤⟨ +-monoˡ-≤ (size t₁) (size-positive t₂) ⟩
        size t₂ + size t₁
      ≡⟨ +-comm (size t₂) (size t₁) ⟩
        size t₁ + size t₂
      ∎)

    <ˢ-app₂ : ∀ t₁ t₂ → t₂ <ˢ t₁ $ t₂
    <ˢ-app₂ t₁ t₂ = ≤-step
      (begin
        suc (size t₂)
      ≤⟨ +-monoˡ-≤ (size t₂) (size-positive t₁) ⟩
        size t₁ + size t₂
      ∎)

    <ˢ-wellFounded : WellFounded _<ˢ_
    <ˢ-wellFounded = InverseImage.wellFounded size <-wellFounded

    indSize : ∀ {ℓ} {P : Pred Term ℓ} → InductionPrinciple _<ˢ_ P
    indSize = indWith <ˢ-wellFounded


---------------------------------------------------------------------------------------------------------------
--
-- Decidable equality.

    _≟ᵀ_ : Decidable {A = Term} _≡_
    (ν x)     ≟ᵀ (ν y)        with x ≟ᴺ y
    ... | no x≢y              = no λ where refl → refl ↯ x≢y
    ... | yes refl            = yes refl
    (ν x)     ≟ᵀ (ƛ y ∙ u)    = no λ ()
    (ν x)     ≟ᵀ (u₁ $ u₂)    = no λ ()
    (ƛ x ∙ t) ≟ᵀ (ν y)        = no λ ()
    (ƛ x ∙ t) ≟ᵀ (ƛ y ∙ u)    with x ≟ᴺ y | t ≟ᵀ u
    ... | no x≢y   | _        = no λ where refl → refl ↯ x≢y
    ... | yes refl | no t≢u   = no λ where refl → refl ↯ t≢u
    ... | yes refl | yes refl = yes refl
    (ƛ x ∙ t) ≟ᵀ (u₁ $ u₂)    = no λ ()
    (t₁ $ t₂) ≟ᵀ (ν y)        = no λ ()
    (t₁ $ t₂) ≟ᵀ (ƛ y ∙ u)    = no λ ()
    (t₁ $ t₂) ≟ᵀ (u₁ $ u₂)    with t₁ ≟ᵀ u₁ | t₂ ≟ᵀ u₂
    ... | no t₁≢u₁ | _        = no λ where refl → refl ↯ t₁≢u₁
    ... | yes refl | no t₂≢u₂ = no λ where refl → refl ↯ t₂≢u₂
    ... | yes refl | yes refl = yes refl

    Term-hasDecidableEquality : HasDecidableEquality Term
    Term-hasDecidableEquality = record { _≟_ = _≟ᵀ_ }


---------------------------------------------------------------------------------------------------------------
--
-- 5.3.2. Definition
-- “The set of _free variables_ of a term `t`, written `fv(t)`, is defined as follows: …”

    fv : Term → UniqueList Name
    fv (ν x)     = [ x ]
    fv (ƛ x ∙ t) = fv t ∖ [ x ]
    fv (t₁ $ t₂) = fv t₁ ∪ fv t₂


---------------------------------------------------------------------------------------------------------------
--
-- 5.3.3. Exercise [⋆⋆]
-- “Give a careful proof that `|fv(t)| ≤ size(t)` for every term `t`.”

    exe533 : ∀ t → length (fv t) ≤ size t
    exe533 (ν x)     = ≤-refl
    exe533 (ƛ x ∙ t) = ≤-step
      (begin
        length (fv t ∖ [ x ])
      ≤⟨ length-untitled (fv t) [ x ] ⟩
        length (fv t)
      ≤⟨ exe533 t ⟩
        size t
      ∎)
    exe533 (t₁ $ t₂) = ≤-step
      (begin
        length (fv t₁ ∪ fv t₂)
      ≤⟨ length-triangular (fv t₁) (fv t₂) ⟩
        length (fv t₁) + length (fv t₂)
      ≤⟨ +-mono-≤ (exe533 t₁) (exe533 t₂) ⟩
        size t₁ + size t₂
      ∎)


---------------------------------------------------------------------------------------------------------------
--
-- • Substitution
--
-- 5.3.4. Convention
-- “Terms that differ only in the names of bound variables are interchangeable in all contexts. …”
-- “This convention renders the substitution operation ‘as good as total,’ since whenever we find ourselves
-- about to apply it to arguments for which it is undefined, we can rename as necessary, so that the side
-- conditions are satisfied.”
--
-- Unfortunately, ‘as good as total’ is not good enough.  We’re going to need to show that we can always get a
-- name that is not in a given set of names.


-- TODO: Write `fresh`.

    postulate
      fresh : UniqueList Name → Name

---------------------------------------------------------------------------------------------------------------
--
-- 5.3.5. Definition [Substitution]
--
-- A direct definition is not recognised by Agda as structurally recursive, because the termination checker
-- doesn’t know that renaming `y` to `z` in `t` is size-preserving.

    private
      module NotGood where
        {-# TERMINATING #-}
        [_↦_]_ : Name → Term → Term → Term
        [ x ↦ s ] (ν y)              with x ≟ᴺ y
        ... | yes refl                = s
        ... | no x≢y                  = ν y
        [ x ↦ s ] (ƛ y ∙ t)          with x ≟ᴺ y | fv s T⟨∌⟩? x
        ... | yes refl | _            = ƛ y ∙ t
        ... | no x≢y   | yes T⟨fvs∌x⟩ = ƛ y ∙ [ x ↦ s ] t
        ... | no x≢y   | no ¬T⟨fvs∌x⟩ = let z = fresh (fv s ∪ fv t) in
                                          ƛ z ∙ [ x ↦ s ] ([ y ↦ ν z ] t)
        [ x ↦ s ] (t₁ $ t₂)          = ([ x ↦ s ] t₁) $ ([ x ↦ s ] t₂)


-- To help the termination checker, we’ll require renaming to supply evidence that size has been preserved, and
-- use this evidence for substitution.  To highlight the similarity between renaming and substitution, we give
-- both using explicit induction on size, even though renaming could also be given directly.

    c-ren : Closed _<ˢ_ (λ t → Name → Name → ∃ λ t′ → size t ≡ size t′)
    c-ren (ν y)     h x z with x ≟ᴺ y
    ... | yes refl        = ν z , refl
    ... | no x≢y          = ν y , refl
    c-ren (ƛ y ∙ t) h x z with x ≟ᴺ y
    ... | yes refl        = ƛ y ∙ t , refl
    ... | no x≢y          = let (t′ , s≡s′) = h {t} (<ˢ-abs x t) x z in
                              ƛ y ∙ t′ , suc & s≡s′
    c-ren (t₁ $ t₂) h x z = let (t₁′ , s₁≡s₁′) = h {t₁} (<ˢ-app₁ t₁ t₂) x z
                                (t₂′ , s₂≡s₂′) = h {t₂} (<ˢ-app₂ t₁ t₂) x z in
                              t₁′ $ t₂′ , (λ s₁ s₂ → suc (s₁ + s₂)) & s₁≡s₁′ ⊗ s₂≡s₂′

    ren : ∀ (t : Term) → Name → Name → ∃ λ t′ → size t ≡ size t′
    ren = indSize c-ren

    c-sub : Closed _<ˢ_ (λ t → Name → Term → Term)
    c-sub (ν y)     h x s         with x ≟ᴺ y
    ... | yes refl                = s
    ... | no x≢y                  = ν y
    c-sub (ƛ y ∙ t) h x s         with x ≟ᴺ y | fv s T⟨∌⟩? x
    ... | yes refl | _            = ƛ y ∙ t
    ... | no x≢y   | yes T⟨fvs∌x⟩ = ƛ y ∙ h {t} (<ˢ-abs x t) x s
    ... | no x≢y   | no ¬T⟨fvs∌x⟩ = let z = fresh (fv s ∪ fv t)
                                        (t′ , s≡s′) = ren t y z in
                                      ƛ z ∙ h {t′} (<ˢ-abs′ x t t′ s≡s′) x s
    c-sub (t₁ $ t₂) h x s         = h {t₁} (<ˢ-app₁ t₁ t₂) x s $ h {t₂} (<ˢ-app₂ t₁ t₂) x s

    sub : ∀ (t : Term) → Name → Term → Term
    sub = indSize c-sub

    [_↦_]_ : Name → Term → Term → Term
    [ x ↦ s ] t = sub t x s

    {-# DISPLAY c-sub t _ x s = [ x ↦ s ] t #-}


---------------------------------------------------------------------------------------------------------------
--
-- • Operational semantics

module FunctionsGetStuck
  where
    open Functions public

    data Value : Pred₀ Term where
      ƛ_∙_ : ∀ (x : Name) (t : Term) → Value (ƛ x ∙ t)


-- Echo of Definition 3.5.3.

    infix 3 _⇒_
    data _⇒_ : Rel₀ Term where
      r-app₁   : ∀ {t₁ t₂ u₁} → t₁ ⇒ u₁ → t₁ $ t₂ ⇒ u₁ $ t₂
      r-app₂   : ∀ {t₁ t₂ u₂} → (vₜ₁ : Value t₁) → t₂ ⇒ u₂ → t₁ $ t₂ ⇒ t₁ $ u₂
      r-appAbs : ∀ {x t₁ t₂} → (vₜ₂ : Value t₂) → (ƛ x ∙ t₁) $ t₂ ⇒ [ x ↦ t₂ ] t₁


-- Echo of Definition 3.5.6.

    open A201901.Chapter3.NormalForms _⇒_ public


-- Echo of Theorem 3.5.7.

    v→nf : ∀ {t} → Value t → NormalForm t
    v→nf (ƛ x ∙ t) = λ ()


-- Echo of Theorem 3.5.4.

    ⇒-det : ∀ {t u u′} → t ⇒ u → t ⇒ u′ → u ≡ u′
    ⇒-det (r-app₁ t₁⇒u₁)     (r-app₁ t₁⇒u₁′)     = (_$ _) & ⇒-det t₁⇒u₁ t₁⇒u₁′
    ⇒-det (r-app₁ t₁⇒u₁)     (r-app₂ vₜ₁ _)       = t₁⇒u₁ ↯ v→nf vₜ₁
    ⇒-det (r-app₁ ())         (r-appAbs _)
    ⇒-det (r-app₂ vₜ₁ _)      (r-app₁ t₁⇒u₁)      = t₁⇒u₁ ↯ v→nf vₜ₁
    ⇒-det (r-app₂ vₜ₁ t₂⇒u₂) (r-app₂ vₜ₂ t₂⇒u₂′) = (_ $_) & ⇒-det t₂⇒u₂ t₂⇒u₂′
    ⇒-det (r-app₂ _ t₂⇒u₂)   (r-appAbs vₜ₂)       = t₂⇒u₂ ↯ v→nf vₜ₂
    ⇒-det (r-appAbs _)        (r-app₁ ())
    ⇒-det (r-appAbs vₜ₂)      (r-app₂ _ t₂⇒u₂)    = t₂⇒u₂ ↯ v→nf vₜ₂
    ⇒-det (r-appAbs vₜ₂)      (r-appAbs vₜ₂′)      = refl


-- Every term is either stuck, a value, or reducible to another term.

    Stuck : Pred₀ Term
    Stuck t = ¬ Value t × NormalForm t

    data Stuck/Value/Reducible : Pred₀ Term where
      stu : ∀ {t} → (σₜ : Stuck t) → Stuck/Value/Reducible t
      val : ∀ {t} → (vₜ : Value t) → Stuck/Value/Reducible t
      red : ∀ {t} → (rₜ : Reducible t) → Stuck/Value/Reducible t

    σ-appStuck₁ : ∀ {t₁ t₂} → Stuck t₁ → Stuck (t₁ $ t₂)
    σ-appStuck₁ (¬vₜ₁ , nfₜ₁) = (λ ())
                              , (λ where (r-app₁ t₁⇒u₁)     → t₁⇒u₁ ↯ nfₜ₁
                                         (r-app₂ vₜ₁ t₂⇒u₂) → vₜ₁ ↯ ¬vₜ₁
                                         (r-appAbs vₜ₂)      → (ƛ _ ∙ _) ↯ ¬vₜ₁)

    σ-appStuck₂ : ∀ {t₁ t₂} → Value t₁ → Stuck t₂ → Stuck (t₁ $ t₂)
    σ-appStuck₂ vₜ₁ (¬vₜ₂ , nfₜ₂) = (λ ())
                                  , (λ where (r-app₁ t₁⇒u₁)     → t₁⇒u₁ ↯ v→nf vₜ₁
                                             (r-app₂ vₜ₁ t₂⇒u₂) → t₂⇒u₂ ↯ nfₜ₂
                                             (r-appAbs vₜ₂)      → vₜ₂ ↯ ¬vₜ₂)

    classify : ∀ t → Stuck/Value/Reducible t
    classify (ν x)     = stu ((λ ()) , (λ ()))
    classify (ƛ x ∙ t) = val (ƛ x ∙ t)
    classify (t₁ $ t₂) with classify t₁ | classify t₂
    ... | stu σₜ₁          | _                = stu (σ-appStuck₁ σₜ₁)
    ... | val vₜ₁          | stu σₜ₂          = stu (σ-appStuck₂ vₜ₁ σₜ₂)
    ... | val (ƛ _ ∙ _)    | val vₜ₂          = red (_ , r-appAbs vₜ₂)
    ... | val vₜ₁          | red (_ , t₂⇒u₂) = red (_ , r-app₂ vₜ₁ t₂⇒u₂)
    ... | red (_ , t₁⇒u₁) | _                = red (_ , r-app₁ t₁⇒u₁)


-- Echo of Theorem 3.5.8.

    data Stuck/Value : Pred₀ Term where
      stu : ∀ {t} → (σₜ : Stuck t) → Stuck/Value t
      val : ∀ {t} → (vₜ : Value t) → Stuck/Value t

    nf→σ/v : ∀ {t} → NormalForm t → Stuck/Value t
    nf→σ/v {t} nfₜ  with classify t
    ... | stu σₜ         = stu σₜ
    ... | val vₜ         = val vₜ
    ... | red (_ , t⇒u) = t⇒u ↯ nfₜ


-- Echo of Definition 3.5.9 and Theorem 3.5.11.

    open A201901.Chapter3.MultiStepReduction _⇒_ public
    open A201901.Chapter3.UniquenessOfNormalForms _⇒_ ⇒-det public

    rs-app₁ : ∀ {t₁ t₂ u₁} → t₁ ⇒* u₁ → t₁ $ t₂ ⇒* u₁ $ t₂
    rs-app₁ = map r-app₁

    rs-app₂ : ∀ {t₁ t₂ u₂} → (vₜ₁ : Value t₁) → t₂ ⇒* u₂ → t₁ $ t₂ ⇒* t₁ $ u₂
    rs-app₂ vₜ₁ = map (r-app₂ vₜ₁)

    rs-appAbs : ∀ {x t₁ t₂} → (vₜ₂ : Value t₂) → (ƛ x ∙ t₁) $ t₂ ⇒* [ x ↦ t₂ ] t₁
    rs-appAbs vₜ₂ = r-appAbs vₜ₂ ◅ ε


---------------------------------------------------------------------------------------------------------------
--
-- 5.3.6. Exercise [⋆⋆]
-- “Adapt these rules to describe the other strategies for evaluation—full beta-reduction, normal-order, and
-- lazy evaluation.”
--
-- In some printings, the solution included in the book is missing the `r-abs` congruence rule, both for full
-- β-reduction and for normal order reduction.

module Strategy-FullBetaReduction
  where
    open Functions public

    infix 3 _⇒_
    data _⇒_ : Rel₀ Term where
      r-app₁   : ∀ {t₁ t₂ u₁} → t₁ ⇒ u₁ → t₁ $ t₂ ⇒ u₁ $ t₂
      r-app₂   : ∀ {t₁ t₂ u₂} → t₂ ⇒ u₂ → t₁ $ t₂ ⇒ t₁ $ u₂
      r-abs    : ∀ {x t u} → t ⇒ u → ƛ x ∙ t ⇒ ƛ x ∙ u
      r-appAbs : ∀ {x t₁ t₂} → (ƛ x ∙ t₁) $ t₂ ⇒ [ x ↦ t₂ ] t₁

    open A201901.Chapter3.NormalForms _⇒_ public


module Strategy-NormalOrder
  where
    open Functions public

    mutual
      data NormalForm : Pred₀ Term where
        ƛ_∙_ : ∀ x {t} → (nfₜ : NormalForm t) → NormalForm (ƛ x ∙ t)
        nanf : ∀ {t} → (nanfₜ : NonAbstractionNormalForm t) → NormalForm t

      data NonAbstractionNormalForm : Pred₀ Term where
        ν   : ∀ x → NonAbstractionNormalForm (ν x)
        _$_ : ∀ {t₁ t₂} → (nanfₜ₁ : NonAbstractionNormalForm t₁) (nfₜ₂ : NormalForm t₂) →
              NonAbstractionNormalForm (t₁ $ t₂)

    data NonAbstraction : Pred₀ Term where
      ν   : ∀ x → NonAbstraction (ν x)
      _$_ : ∀ t₁ t₂ → NonAbstraction (t₁ $ t₂)

    infix 3 _⇒_
    data _⇒_ : Rel₀ Term where
      r-app₁   : ∀ {t₁ t₂ u₁} → (naₜ₁ : NonAbstraction t₁) (naᵤ₁ : NonAbstraction u₁) → t₁ ⇒ u₁ →
                 t₁ $ t₂ ⇒ u₁ $ t₂
      r-app₂   : ∀ {t₁ t₂ u₂} → (nanfₜ₁ : NonAbstractionNormalForm t₁) → t₂ ⇒ u₂ → t₁ $ t₂ ⇒ t₁ $ u₂
      r-abs    : ∀ {x t u} → t ⇒ u → ƛ x ∙ t ⇒ ƛ x ∙ u
      r-appAbs : ∀ {x t₁ t₂} → (ƛ x ∙ t₁) $ t₂ ⇒ [ x ↦ t₂ ] t₁

    open A201901.Chapter3.NormalForms _⇒_ public
      renaming (NormalForm to NegativeNormalForm ; nf→¬r to nnf→¬r ; ¬r→nf to ¬r→nnf)

    mutual
      nf→nnf : ∀ {t} → NormalForm t → NegativeNormalForm t
      nf→nnf (ƛ _ ∙ nfₜ)  = λ where (r-abs t⇒u) → t⇒u ↯ nf→nnf nfₜ
      nf→nnf (nanf nanfₜ) = nanf→nnf nanfₜ

      nanf→nnf : ∀ {t} → NonAbstractionNormalForm t → NegativeNormalForm t
      nanf→nnf (ν _)           = λ ()
      nanf→nnf (nanfₜ₁ $ nfₜ₂) = λ where
        (r-app₁ naₜ₁ naᵤ₁ t₁⇒u₁) → t₁⇒u₁ ↯ nanf→nnf nanfₜ₁
        (r-app₂ nanfₜ₁ t₂⇒u₂)    → t₂⇒u₂ ↯ nf→nnf nfₜ₂
        r-appAbs                  → case nanfₜ₁ of λ where ()

    ⇒-det : ∀ {t u u′} → t ⇒ u → t ⇒ u′ → u ≡ u′
    ⇒-det (r-app₁ _ _ t₁⇒u₁) (r-app₁ _ _ t₁⇒u₁′) = (_$ _) & ⇒-det t₁⇒u₁ t₁⇒u₁′
    ⇒-det (r-app₁ _ _ t₁⇒u₁) (r-app₂ nanfₜ₁ _)    = t₁⇒u₁ ↯ nanf→nnf nanfₜ₁
    ⇒-det (r-app₁ () _ _)     r-appAbs
    ⇒-det (r-app₂ nanfₜ₁ _)   (r-app₁ _ _ t₁⇒u₁′) = t₁⇒u₁′ ↯ nanf→nnf nanfₜ₁
    ⇒-det (r-app₂ _ t₂⇒u₂)   (r-app₂ _ t₂⇒u₂′)   = (_ $_) & ⇒-det t₂⇒u₂ t₂⇒u₂′
    ⇒-det (r-app₂ () _)       r-appAbs
    ⇒-det (r-abs t⇒u)        (r-abs t⇒u′)        = (ƛ _ ∙_) & ⇒-det t⇒u t⇒u′
    ⇒-det r-appAbs            (r-app₁ () _ _)
    ⇒-det r-appAbs            (r-app₂ () _)
    ⇒-det r-appAbs            r-appAbs             = refl

    open A201901.Chapter3.MultiStepReduction _⇒_ public
    open A201901.Chapter3.UniquenessOfNormalForms _⇒_ ⇒-det public


module Strategy-Lazy/CallByName
  where
    open Functions public

    infix 3 _⇒_
    data _⇒_ : Rel₀ Term where
      r-app₁   : ∀ {t₁ t₂ u₁} → t₁ ⇒ u₁ → t₁ $ t₂ ⇒ u₁ $ t₂
      r-appAbs : ∀ {x t₁ t₂} → (ƛ x ∙ t₁) $ t₂ ⇒ [ x ↦ t₂ ] t₁

    data Value : Pred₀ Term where
      ƛ_∙_ : ∀ (x : Name) (t : Term) → Value (ƛ x ∙ t)

    open A201901.Chapter3.NormalForms _⇒_ public

    v→nf : ∀ {t} → Value t → NormalForm t
    v→nf (ƛ x ∙ t) = λ ()

    ⇒-det : ∀ {t u u′} → t ⇒ u → t ⇒ u′ → u ≡ u′
    ⇒-det (r-app₁ t₁⇒u₁) (r-app₁ t₁⇒u₁′) = (_$ _) & ⇒-det t₁⇒u₁ t₁⇒u₁′
    ⇒-det (r-app₁ ())     r-appAbs
    ⇒-det r-appAbs        (r-app₁ ())
    ⇒-det r-appAbs        r-appAbs         = refl

    open A201901.Chapter3.MultiStepReduction _⇒_ public
    open A201901.Chapter3.UniquenessOfNormalForms _⇒_ ⇒-det public


---------------------------------------------------------------------------------------------------------------
--
-- 5.3.7. Exercise [⋆⋆ ↛]
-- “Exercise 3.5.16 gave an alternate presentation of the operational semantics of booleans and arithmetic
-- expressions in which stuck terms are defined to evaluate to a special constant `wrong`.  Extend this
-- semantics to λNB.”
-- (skipped)


---------------------------------------------------------------------------------------------------------------
--
-- 5.3.8. Exercise [⋆⋆]
-- “Exercise 4.2.2 introduced a “big-step” style of evaluation for arithmetic expressions, where the basic
-- evaluation relation is ‘term `t` evaluates to final result `v`’.  Show how to formulate the evaluation rules
-- for lambda-terms in the big-step style.”

private
  module Exercise538 where
    open FunctionsGetStuck

    infix 3 _⇓_
    data _⇓_ : Rel₀ Term where
      e-val    : ∀ {t} → (vₜ : Value t) → t ⇓ t
      e-appAbs : ∀ {x t₁ t₂ u₁ u₂ u} → (vᵤ₂ : Value u₂) → t₁ ⇓ ƛ x ∙ u₁ → t₂ ⇓ u₂ → [ x ↦ u₂ ] u₁ ⇓ u →
                 t₁ $ t₂ ⇓ u


-- As an exercise, we show that the small-step and big-step semantics coincide.

    exe568-ltr : ∀ {t u} → (vᵤ : Value u) → t ⇓ u → t ⇒* u
    exe568-ltr vᵤ (e-val vₜ)
        = ε
    exe568-ltr vᵤ (e-appAbs vᵤ₂ t₁⇓ƛx∙u₁ t₂⇓u₂ [x↦u₂]u₁⇓u)
        = rs-app₁ (exe568-ltr (ƛ _ ∙ _) t₁⇓ƛx∙u₁) ◅◅
          rs-app₂ (ƛ _ ∙ _) (exe568-ltr vᵤ₂ t₂⇓u₂) ◅◅
          rs-appAbs vᵤ₂ ◅◅
          (exe568-ltr vᵤ [x↦u₂]u₁⇓u)

    lem-app₁ : ∀ {t₁ t₂ u} → (vᵤ : Value u) → t₁ $ t₂ ⇒* u →
               ∃ λ x → ∃ λ u₁ → ∃ λ u₂ → Value u₂ × t₁ ⇒* ƛ x ∙ u₁ × t₂ ⇒* u₂ × [ x ↦ u₂ ] u₁ ⇒* u
    lem-app₁ () ε
    lem-app₁ vᵤ (r-app₁ t₁⇒i₁ ◅ i₁$t₂⇒*u) with lem-app₁ vᵤ i₁$t₂⇒*u
    ... | x , u₁ , u₂ , vᵤ₂ , i₁⇒*ƛx∙u₁          , t₂⇒*u₂ , [x↦u₂]u₁⇒*u
        = x , u₁ , u₂ , vᵤ₂ , t₁⇒i₁ ◅ i₁⇒*ƛx∙u₁ , t₂⇒*u₂ , [x↦u₂]u₁⇒*u
    lem-app₁ vᵤ (r-app₂ vₜ₁ t₂⇒i₂ ◅ t₁$i₂⇒*u) with lem-app₁ vᵤ t₁$i₂⇒*u
    ... | x , u₁ , u₂ , vᵤ₂ , t₁⇒*ƛx∙u₁ , i₂⇒*u₂          , [x↦u₂]u₁⇒*u
        = x , u₁ , u₂ , vᵤ₂ , t₁⇒*ƛx∙u₁ , t₂⇒i₂ ◅ i₂⇒*u₂ , [x↦u₂]u₁⇒*u
    lem-app₁ vᵤ (r-appAbs {x} {t₁} vₜ₂ ◅ [x↦t₂]t₁⇒*u)
        = x , t₁ , _ , vₜ₂ , ε , ε , [x↦t₂]t₁⇒*u

    lem-app₂ : ∀ {x t₂ u u₁} → (vᵤ : Value u) → ƛ x ∙ u₁ $ t₂ ⇒* u →
               ∃ λ u₂ → Value u₂ × t₂ ⇒* u₂ × [ x ↦ u₂ ] u₁ ⇒* u
    lem-app₂ () ε
    lem-app₂ vᵤ (r-app₁ () ◅ _)
    lem-app₂ vᵤ (r-app₂ vₜ₁ t₂⇒i₂ ◅ t₁$i₂⇒*u) with lem-app₂ vᵤ t₁$i₂⇒*u
    ... | u₂ , vᵤ₂ , i₂⇒*u₂ , [x↦u₂]t₁⇒*u
        = u₂ , vᵤ₂ , t₂⇒i₂ ◅ i₂⇒*u₂ , [x↦u₂]t₁⇒*u
    lem-app₂ vᵤ (r-appAbs vₜ₂ ◅ [x↦t₂]t₁⇒*u)
        = _ , vₜ₂ , ε , [x↦t₂]t₁⇒*u


-- TODO: Rewrite `exe568-rtl` using explicit induction.

    private
      module NotGood where
        {-# TERMINATING #-}
        exe568-rtl : ∀ {t u} → (vᵤ : Value u) → t ⇒* u → t ⇓ u
        exe568-rtl vᵤ ε
            = e-val vᵤ
        exe568-rtl vᵤ (r-app₁ t₁⇒i₁ ◅ i₁$t₂⇒*u) with lem-app₁ vᵤ i₁$t₂⇒*u
        ... | x , u₁ , u₂ , vᵤ₂ , i₁⇒*ƛx∙u₁ , t₂⇒*u₂ , [x↦u₂]u₁⇒*u
            = e-appAbs vᵤ₂ (exe568-rtl (ƛ _ ∙ _) (t₁⇒i₁ ◅ i₁⇒*ƛx∙u₁))
                           (exe568-rtl vᵤ₂ t₂⇒*u₂)
                           (exe568-rtl vᵤ [x↦u₂]u₁⇒*u)
        exe568-rtl vᵤ (r-app₂ vₜ₁@(ƛ _ ∙ _) t₂⇒i₂ ◅ t₁$i₂⇒*u) with lem-app₂ vᵤ t₁$i₂⇒*u
        ... | u₂ , vᵤ₂ , i₂⇒*u₂ , [x↦u₂]u₁⇒*u
            = e-appAbs vᵤ₂ (e-val vₜ₁)
                           (exe568-rtl vᵤ₂ (t₂⇒i₂ ◅ i₂⇒*u₂))
                           (exe568-rtl vᵤ [x↦u₂]u₁⇒*u)
        exe568-rtl vᵤ (r-appAbs vₜ₂ ◅ [x↦t₂]t₁⇒*u)
            = e-appAbs vₜ₂ (e-val (ƛ _ ∙ _))
                           (e-val vₜ₂)
                           (exe568-rtl vᵤ [x↦t₂]t₁⇒*u)

        exe568 : ∀ {t u} → (vᵤ : Value u) → (t ⇓ u) ↔ (t ⇒* u)
        exe568 vᵤ = exe568-ltr vᵤ , exe568-rtl vᵤ


---------------------------------------------------------------------------------------------------------------
--
-- 5.4. Notes


---------------------------------------------------------------------------------------------------------------