PLFA agda exercises
{-# OPTIONS --guardedness --sized-types #-}

module Main where

open import Level using (0ℓ)
open import Size
open import Data.Bool.Base using (Bool; true; false; if_then_else_)
open import Data.Nat.Base using (ℕ; zero; suc)
open import Data.Fin
import Data.Fin.Show as Fin
open import Data.String.Base using (String; _++_; parens)
open import Data.Unit.Polymorphic.Base using (⊤)
open import Codata.Sized.Thunk
open import Function.Base using (_$_; _∘_; id)
open import Relation.Nullary

open import Codata.Musical.Notation
open import Codata.Sized.Colist using (Colist; _∷_; [])
open import Codata.Musical.Colist renaming (Colist to Colist♩) using (_∷_; [])
open import Codata.Musical.Conversion

variable
  i : Size
  m n : ℕ
  A : Set

data Lam (n : ℕ) : Set where
  var : Fin n → Lam n
  app : Lam n → Lam n → Lam n
  lam : Lam (suc n) → Lam n

data Loc : Set where appL appR lam : Loc

appParens : Loc → String → String
appParens appR str = parens str
appParens _ str = str

lamParens : Loc → String → String
lamParens lam str = str
lamParens _ str = parens str

show : Loc → Lam n → String
show i (var v)   = Fin.show v
show i (app f t) = appParens i $ show appL f ++ " " ++ show appR t
show i (lam b)   = lamParens i $ "λ " ++ show lam b

variable
  b f t : Lam n

_∙_ : (Fin m → A) → A → Fin (suc m) → A
(ρ ∙ v) zero    = v
(ρ ∙ v) (suc k) = ρ k

rename : (Fin m → Fin n) → Lam m → Lam n
rename ρ (var v)   = var (ρ v)
rename ρ (app f t) = app (rename ρ f) (rename ρ t)
rename ρ (lam b)   = lam (rename ((suc ∘ ρ) ∙ zero) b)

subst : (Fin m → Lam n) → Lam m → Lam n
subst ρ (var v)   = ρ v
subst ρ (app f t) = app (subst ρ f) (subst ρ t)
subst ρ (lam b)   = lam (subst ((rename suc ∘ ρ) ∙ var zero) b)

data Redex {n} : Lam n → Set where
  here : ∀ b t → Redex (app (lam b) t)
  lam  : Redex b → Redex (lam b)
  appL : Redex f → ∀ t → Redex (app f t)
  appR : ∀ f → Redex t → Redex (app f t)

redex : ∀ {n} (t : Lam n) → Dec (Redex t)
redex (var v)             = no (λ ())
redex (app (lam b) t)     = yes (here b t)
redex (app f@(var _) t)   with redex t
... | yes rt = yes (appR f rt)
... | no nrt = no λ where (appR _ rt) → nrt rt
redex (app f@(app _ _) t) with redex f | redex t
... | yes rf | _ = yes (appL rf t)
... | _ | yes rt = yes (appR f rt)
... | no nrf | no nrt = no λ where
  (appL rf _) → nrf rf
  (appR _ rt) → nrt rt
redex (lam b) with redex b
... | yes rb = yes (lam rb)
... | no nrb = no λ where (lam rb) → nrb rb

fire : Redex {n} t → Lam n
fire (here b t)  = subst (var ∙ t) b
fire (lam rt)    = lam (fire rt)
fire (appL rf t) = app (fire rf) t
fire (appR f rt) = app f (fire rt)

eval : Lam n → Colist (Lam n) i
eval t with redex t
... | yes rt = t ∷ λ where .force → eval (fire rt)
... | no nrt = t ∷ λ where .force → []

open import IO.Base
open import IO.Finite

trace : Colist♩ (Lam n) → IO {0ℓ} ⊤
trace []       = pure _
trace (t ∷ ts) = seq (♯ putStrLn (show lam t))
                     (♯ trace (♭ ts))

`id : Lam 0
`id = lam (var (# 0))

main : Main
main = run $ do
  trace (Colist.toMusical $ eval `id)
  trace (Colist.toMusical $ eval (app `id (app (app `id `id) (app `id `id))))