Skip to content

Instantly share code, notes, and snippets.

@cxandru
Created January 12, 2026 12:40
Show Gist options
  • Select an option

  • Save cxandru/91b1497ca735bb251b688add18512c39 to your computer and use it in GitHub Desktop.

Select an option

Save cxandru/91b1497ca735bb251b688add18512c39 to your computer and use it in GitHub Desktop.
Alexandru's recursion razor
The question is how to encode the mutual nested recursion happening in "razor" using structured recursion (ideally recursive coalgebras)
\begin{code}
open import Level using (Level;_⊔_;0ℓ)
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality.Core
module Razor where
open import Data.Nat
open import Data.Product
open import Function.Base
open import Data.String
mutual
data Expr : Set where
_:+:_ : Expr → Expr → Expr
Var : String → Expr
Lit : ℕ → Expr
Let_In : Assgt → Expr → Expr
data Assgt : Set where
_↦_ : String → Expr → Assgt
Env : Set
Env = String → ℕ
\end{code}
%<*razor>
\begin{code}
mutual
evA : Env → Assgt → Env
evE : Env → Expr → ℕ
evA env ( x ↦ expr ) = λ y → case x ≈? y of
λ{ (yes _) → evE env expr
; (no _) → env y }
evE env (x :+: y) = evE env x + evE env y
evE env (Var x) = env x
evE env (Lit n) = n
evE env (Let assgt In expr) = evE (evA env assgt) expr
\end{code}
%</razor>
\begin{code}
-- decode the mutual recursion:
data EoA : Set where
E : EoA
A : EoA
data AST : EoA → Set where
_:+:_ : AST E → AST E → AST E
Var : String → AST E
Lit : ℕ → AST E
Let_In : AST A → AST E → AST E
_↦_ : String → AST E → AST A
ev : Env → {i : EoA} → AST i → (case i of λ{A → Env ; E → ℕ})
ev env (x :+: y) = ev env x + ev env y
ev env (Var x) = env x
ev env (Lit n) = n
ev env (Let assgt In expr) = ev (ev env assgt) expr
ev env (x ↦ expr) = λ y → case x ≈? y of
λ{ (yes _) → ev env expr
; (no _) → env y }
\end{code}
@NathanielB123
Copy link
Copy Markdown

NathanielB123 commented Jan 22, 2026

As a fold:

open import Agda.Builtin.Bool renaming (true to tt; false to ff)
open import Agda.Builtin.Nat renaming (suc to su; zero to ze)

module MutualRec (Name : Set) (_≟_ : Name  Name  Bool) where

if_then_else_ :  {A : Set}  Bool  A  A  A
if tt then u else v = u
if ff then u else v = v

mutual
  data Expr : Set where
    _:+:_ : Expr  Expr  Expr
    Var : Name  Expr
    Lit : Nat  Expr
    Let_In : Assgt  Expr  Expr
  data Assgt : Set where
    _↦_ : Name  Expr  Assgt

record Model : Set₁ where field
  Exprᴹ   : Set
  Assgtᴹ  : Set
  _:+:ᴹ_  : Exprᴹ  Exprᴹ  Exprᴹ
  Varᴹ    : Name  Exprᴹ
  Litᴹ    : Nat  Exprᴹ
  Letᴹ_In : Assgtᴹ  Exprᴹ  Exprᴹ
  _↦ᴹ_    : Name  Exprᴹ  Assgtᴹ

module _ (𝕄 : Model) where
  open Model 𝕄
  recExpr  : Expr  Exprᴹ
  recAssgt : Assgt  Assgtᴹ

  recExpr (e₁ :+: e₂)  = recExpr e₁ :+:ᴹ recExpr e₂
  recExpr (Var n)      = Varᴹ n
  recExpr (Lit n)      = Litᴹ n
  recExpr (Let a In e) = Letᴹ recAssgt a In (recExpr e)

  recAssgt (x ↦ e) = x ↦ᴹ recExpr e

Env : Set
Env = Name  Nat

open Model

ev𝕄 : Model
ev𝕄 .Exprᴹ  = Env  Nat
ev𝕄 .Assgtᴹ = Env  Env
ev𝕄 ._:+:ᴹ_ e₁ᴹ e₂ᴹ ρ = e₁ᴹ ρ + e₁ᴹ ρ
ev𝕄 .Varᴹ x ρ = ρ x
ev𝕄 .Litᴹ n ρ = n
ev𝕄 .Letᴹ_In aᴹ eᴹ ρ = eᴹ (aᴹ ρ)
ev𝕄 ._↦ᴹ_ x eᴹ ρ y = if x ≟ y then eᴹ ρ else ρ y

evE : Env  Expr  Nat
evA : Env  Assgt  Env

evE ρ e = recExpr  ev𝕄 e ρ
evA ρ a = recAssgt ev𝕄 a ρ

@cxandru

@lawcho
Copy link
Copy Markdown

lawcho commented Jan 30, 2026

Simplified razor with 1 recursive function and simple types:

open import Agda.Builtin.Bool
open import Agda.Builtin.Nat

_&&_ : Bool  Bool  Bool
true && true = true
_ && _ = false

data Tree : Set where
  leaf : Nat  Tree
  branch : Tree  Tree  Tree
  subst : (Nat  Tree)  Tree  Tree

all : (Nat  Bool)  Tree  Bool
all p (leaf x) = p x
all p (branch t1 t2) = all p t1 && all p t2
all p (subst f t) = all (λ b  all p (f b)) t

@cxandru
Copy link
Copy Markdown
Author

cxandru commented Feb 2, 2026

@NathanielB123 I've translated your definition back into explicitly an algebra for the original functor:

ASTENV : EoA  Set
ASTENV = λ{A  Env  Env ; E  Env  ℕ}

module foo where
  data ASTF (r : EoA  Set) : EoA  Set where
    _:+:_ : r E  r E  ASTF r E
    Var : String  ASTF r E
    Lit : ASTF r E
    Let_In : r A {- ev env assgt -}  r E {- ev env expr ↯ -}  ASTF r E
    _↦_ : String  r E  ASTF r A

  -- translation of Nathaniel's soln
  evAlg : {i : EoA}  ASTF ASTENV i  ASTENV i
  evAlg (x :+: x₁) env = x env + x₁ env
  evAlg (Var x) env = env x
  evAlg (Lit n) env = n
  evAlg (Let envt In expr) env = expr (envt env) 
  evAlg (x ↦ x₁) env = λ y  case x ≈? y of
    λ{ (yes  _)  x₁ env
     ; (no   _)  env y }

@cxandru
Copy link
Copy Markdown
Author

cxandru commented Feb 2, 2026

@NathanielB123 @lawcho I feel like there seems to be something going on with eliminating nestedness by eliminating to higher types (the hierarchy here as in System T, i.e. every arrow left or right increases the level of the type), since this is also how one does it for Ackermann

@NathanielB123
Copy link
Copy Markdown

Ooh that's an interesting point. So I guess the concrete question is: can you write this interpreter with only primitive recursion? I'm not sure...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment