Alpha equivalence checker for arbitrary lambda terms
7

Configure Feed

Select the types of activity you want to include in your feed.

ego / ocaml / lib / locally_nameless.ml
5.0 kB 196 lines
1type t = 2 | BVar of int 3 | FVar of string 4 | Lam of t 5 | App of t * t 6 7module SSet = Set.Make (String) 8 9exception Not_locally_closed 10 11let rec free_variables = function 12 | BVar _ -> SSet.empty 13 | FVar x -> SSet.singleton x 14 | Lam b -> free_variables b 15 | App (f, a) -> SSet.union (free_variables f) (free_variables a) 16 17let free_variable_list t = SSet.elements (free_variables t) 18 19let rec pretty = function 20 | BVar i -> "#" ^ string_of_int i 21 | FVar x -> x 22 | Lam b -> "\\. " ^ pretty b 23 | App (f, a) -> 24 let pf = 25 match f with 26 | BVar _ | FVar _ | App _ -> pretty f 27 | _ -> "(" ^ pretty f ^ ")" 28 in 29 let pa = 30 match a with BVar _ | FVar _ -> pretty a | _ -> "(" ^ pretty a ^ ")" 31 in 32 pf ^ " " ^ pa 33 34let rec open_at k x = function 35 | BVar i -> if i = k then FVar x else BVar i 36 | FVar y -> FVar y 37 | Lam b -> Lam (open_at (k + 1) x b) 38 | App (f, a) -> App (open_at k x f, open_at k x a) 39 40let open_top t x = open_at 0 x t 41 42let rec close_at k x = function 43 | BVar i -> BVar i 44 | FVar y -> if y = x then BVar k else FVar y 45 | Lam b -> Lam (close_at (k + 1) x b) 46 | App (f, a) -> App (close_at k x f, close_at k x a) 47 48let close_top t x = close_at 0 x t 49 50let rec lc_at depth = function 51 | BVar i -> i < depth 52 | FVar _ -> true 53 | Lam b -> lc_at (depth + 1) b 54 | App (f, a) -> lc_at depth f && lc_at depth a 55 56let lc t = lc_at 0 t 57 58let require_lc t = 59 if not (lc t) then raise Not_locally_closed 60 61let rec subst_bvar j s = function 62 | BVar i -> if i = j then s else if i > j then BVar (i - 1) else BVar i 63 | FVar x -> FVar x 64 | Lam b -> Lam (subst_bvar (j + 1) (shift s) b) 65 | App (f, a) -> App (subst_bvar j s f, subst_bvar j s a) 66 67and shift t = 68 let rec go cutoff = function 69 | BVar i -> if i >= cutoff then BVar (i + 1) else BVar i 70 | FVar x -> FVar x 71 | Lam b -> Lam (go (cutoff + 1) b) 72 | App (f, a) -> App (go cutoff f, go cutoff a) 73 in 74 go 0 t 75 76let rec subst_fvar x s = function 77 | BVar i -> BVar i 78 | FVar y -> if y = x then s else FVar y 79 | Lam b -> Lam (subst_fvar x s b) 80 | App (f, a) -> App (subst_fvar x s f, subst_fvar x s a) 81 82let convert_from_named t = 83 let rec go env = function 84 | Named.Var x -> begin 85 match List.assoc_opt x env with 86 | Some i -> BVar i 87 | None -> FVar x 88 end 89 | Named.Lam (x, b) -> 90 Lam (go ((x, 0) :: List.map (fun (v, i) -> (v, i + 1)) env) b) 91 | Named.App (f, a) -> App (go env f, go env a) 92 | Named.Subst (x, s, b) -> 93 go env (Named.substitute x s b) 94 in 95 go [] t 96 97let convert_to_named t = 98 require_lc t; 99 let fv = free_variables t in 100 let fresh_avoid avoid base = 101 if not (SSet.mem base avoid) then base 102 else 103 let rec go i = 104 let cand = base ^ string_of_int i in 105 if SSet.mem cand avoid then go (i + 1) else cand 106 in 107 go 0 108 in 109 let rec go env = function 110 | BVar i -> Named.Var (List.nth env i) 111 | FVar x -> Named.Var x 112 | Lam b -> 113 let avoid = 114 List.fold_left (fun a x -> SSet.add x a) fv env 115 in 116 let x = fresh_avoid avoid "v" in 117 Named.Lam (x, go (x :: env) b) 118 | App (f, a) -> Named.App (go env f, go env a) 119 in 120 go [] t 121 122let is_value = function Lam _ -> true | _ -> false 123 124let rec step = function 125 | App (Lam b, a) -> Some (subst_bvar 0 a b) 126 | App (f, a) when not (is_value f) -> begin 127 match step f with 128 | Some f' -> Some (App (f', a)) 129 | None -> begin 130 match step a with Some a' -> Some (App (f, a')) | None -> None 131 end 132 end 133 | App (f, a) -> begin 134 match step a with Some a' -> Some (App (f, a')) | None -> None 135 end 136 | Lam b -> begin 137 match step b with Some b' -> Some (Lam b') | None -> None 138 end 139 | _ -> None 140 141let step_limit = 10000 142 143let normalise t = 144 require_lc t; 145 let rec go n t = 146 if n >= step_limit then t 147 else match step t with Some t' -> go (n + 1) t' | None -> t 148 in 149 go 0 t 150 151let normalise_counted t = 152 require_lc t; 153 let count = ref 0 in 154 let maxd = ref 0 in 155 let rec walk d t = 156 incr count; 157 if d > !maxd then maxd := d; 158 match t with 159 | BVar _ | FVar _ -> () 160 | Lam b -> walk (d + 1) b 161 | App (f, a) -> walk (d + 1) f; walk (d + 1) a 162 in 163 walk 0 t; 164 let rec go n t = 165 if n >= step_limit then t 166 else 167 match step t with 168 | Some t' -> walk 0 t'; go (n + 1) t' 169 | None -> t 170 in 171 (go 0 t, !count, !maxd) 172 173let substitute x s body = subst_fvar x s body 174 175let alpha_equivalent t1 t2 = 176 require_lc t1; 177 require_lc t2; 178 normalise t1 = normalise t2 179 180let alpha_equivalent_counted t1 t2 = 181 require_lc t1; 182 require_lc t2; 183 let count = ref 0 in 184 let maxd = ref 0 in 185 let rec go d a b = 186 incr count; 187 if d > !maxd then maxd := d; 188 match (a, b) with 189 | BVar i, BVar j -> i = j 190 | FVar x, FVar y -> x = y 191 | Lam b1, Lam b2 -> go (d + 1) b1 b2 192 | App (f1, a1), App (f2, a2) -> go (d + 1) f1 f2 && go (d + 1) a1 a2 193 | _ -> false 194 in 195 let r = go 0 (normalise t1) (normalise t2) in 196 (r, !count, !maxd)