Zulip Chat Archive

Stream: general

Topic: suggestions for implementing hygien for synthetic language


Frederick Pu (Jan 02 2026 at 22:17):

I'm working on a system based on do notation to create a general system for defining single static assignment IRs. Does anyone have any suggestions on how I should handle hygien (eg for continuation variables kbreak and kcontinue)? I was thinking of collecting all of the mutvars before translating from SSADo to SSAExpr:

import Lean

open Lean

inductive SSABaseType where
| float : SSABaseType
| int : SSABaseType
| unit : SSABaseType

inductive SSAType where
| ofBase : SSABaseType  SSAType
| fun : SSAType  SSAType  SSAType
| prod : SSAType  SSAType  SSAType

inductive SSAConst where
| loop (ty : SSAType) : SSAConst
| ofFloat : Float  SSAConst
| ofInt : Int  SSAConst
| ofUnit : Unit  SSAConst

inductive SSAExpr where
| letE (var : Name) (val : SSAExpr) (body : SSAExpr) : SSAExpr
| var (name : Name) : SSAExpr
| const : SSAConst  SSAExpr
| app (f : SSAExpr) (arg : SSAExpr)
| lam (var : Name) (varType : SSAType) (body : SSAExpr) : SSAExpr
| prod (α : SSAType) (β : SSAType): SSAExpr  SSAExpr  SSAExpr
| ifthenelse (cond t e : SSAExpr) : SSAExpr
deriving Inhabited

inductive SSADo where
| expr : SSAExpr  SSADo
| seq (s₁ s₂ : SSADo) : SSADo
| letE (var : Name) (val : SSAExpr) (rest : SSADo) : SSADo
| letM (var : Name) (val : SSAExpr) (rest : SSADo) : SSADo -- let mut
| assign (var : Name) (val : SSAExpr) (rest : SSADo) : SSADo
| loop (body : SSADo) (rest : SSADo) : SSADo
| break : SSADo
| continue : SSADo
| return (out : SSAExpr) : SSADo
| ifthenelse (cond : SSAExpr) (t e : SSADo) (rest : SSADo) : SSADo

#check Std.HashMap.insert

#check Std.TreeMap

#eval (((Std.HashMap.empty : Std.HashMap Name Int).insert `b 123).insert `a 456).keys


def SSAExpr.inferType : SSAExpr  SSAType := sorry

def VarMap := Array (Name × SSAType)

def mkMutTuple (mutVars : VarMap) : SSAExpr × SSAType := sorry

def destructMutTuple (mutVars : VarMap) (body : SSAExpr) : SSAExpr := sorry

/-
    for fixed mutVars, if baseName1 and baseName2 don't share a prefix then freshName will give different fresh names.
-/
def freshName (mutVars : VarMap) (baseName : Name) : Name := sorry

partial def SSADo.toSSAExpr (mutVars : VarMap) (kbreak kcontinue : Option Name) : SSADo  SSAExpr
| expr (.const (.ofUnit ())) =>
    match kcontinue with
    | some kcontinue => SSAExpr.app (SSAExpr.var kcontinue) (mkMutTuple mutVars).1
    | none => (.const (.ofUnit ()))
-- note: only trailing exprs are interpreted as return types
-- ie: `do if cond then 10 else 10` is invalid but `do if cond then return 10 else (); 10` is valid
| expr e =>
    match kcontinue with
    | some kcontinue => e
    | none => sorry -- loop body should not end in non unit type
| seq s₁ s₂ => SSAExpr.letE `x (s₁.toSSAExpr mutVars kbreak kcontinue) (s₂.toSSAExpr mutVars kbreak kcontinue)
| letE var val rest => SSAExpr.letE var val (rest.toSSAExpr mutVars kbreak kcontinue)
| letM var val rest => SSAExpr.letE var val (rest.toSSAExpr (mutVars.push (var, val.inferType)) kbreak kcontinue)
| assign var val rest => SSAExpr.letE var val (rest.toSSAExpr mutVars kbreak kcontinue)
| loop body rest =>
    let (mutTuple, mutTupleType) := (mkMutTuple mutVars)
    let bodyMutVars : VarMap := sorry
    let nS : Name := freshName (Array.append mutVars bodyMutVars) `s
    let breakNew : SSAExpr := SSAExpr.lam nS mutTupleType <| (destructMutTuple mutVars (rest.toSSAExpr mutVars kbreak kcontinue))
    let nKBreak : Name := freshName mutVars `kbreak
    let nKContinue : Name := freshName mutVars `kcontinue
    -- todo :: modify mutvars passed into toSSAExpr for body
    let body' : SSAExpr := destructMutTuple mutVars (body.toSSAExpr mutVars nKBreak nKContinue)
    SSAExpr.letE nKBreak breakNew <|
        SSAExpr.app (SSAExpr.app (SSAExpr.const (SSAConst.loop mutTupleType)) (SSAExpr.lam nKContinue (SSAType.fun mutTupleType (SSAType.ofBase .unit)) (SSAExpr.lam nS mutTupleType body'))) mutTuple
| .break =>
    match kbreak with
    | some kbreak =>
        let mutTuple : SSAExpr := (mkMutTuple mutVars).1
        SSAExpr.app (SSAExpr.var kbreak) mutTuple
    | none => sorry -- violates grammer
| .continue =>
    match kcontinue with
    | some kcontinue =>
        let mutTuple : SSAExpr := (mkMutTuple mutVars).1
        SSAExpr.app (SSAExpr.var kcontinue) mutTuple
    | none => sorry -- violates grammer
| .return out => out
| ifthenelse cond t e rest =>
    let (mutTuple, mutTupleType) := (mkMutTuple mutVars)
    let nKContinue : Name := freshName mutVars `kcontinue
    let restMutVars : VarMap := sorry
    let nS : Name := freshName (Array.append mutVars restMutVars) `s
    -- todo :: pass expanded mutvars into toSSAExpr
    let continue' := SSAExpr.lam nS mutTupleType <| rest.toSSAExpr mutVars kbreak kcontinue
    SSAExpr.letE nKContinue continue' <|
    SSAExpr.ifthenelse cond (t.toSSAExpr mutVars kbreak nKContinue) (e.toSSAExpr mutVars kbreak nKContinue)

Last updated: Feb 28 2026 at 14:05 UTC