Lean Notes

1.1. Right-associative And🔗

Transform a sequence of Ands into a right-associative form.

Example:

(a ∧ b) ∧ c => a ∧ b ∧ c

Contains versions that use:

  1. assoc_and% - macro_rules

  2. assoc_and_expr% - Expr only

  3. assoc_and_qq% - Qq

namespace AndAssoc namespace Macro /-- A macro version of the expression transform -/ syntax (name := assocAnd) "assoc_and% " term : term macro_rules | `(assoc_and% ($a $b) $c) => `(assoc_and% $a $b $c) | `(assoc_and% $a $b) => `($a assoc_and% $b) | `(assoc_and% ($e)) => `(assoc_and% $e) | `(assoc_and% $e) => `($e) section Test variable (a b c d e: Prop) /-- info: a : Prop -/ #guard_msgs in #check assoc_and% a /-- info: a : Prop -/ #guard_msgs in #check assoc_and% (a) /-- info: a ∧ b ∧ c : Prop -/ #guard_msgs in #check assoc_and% ((a b) c) /-- info: a ∧ b ∧ c : Prop -/ #guard_msgs in #check assoc_and% (a b) c /-- info: a ∧ b ∧ c : Prop -/ #guard_msgs in #check assoc_and% a (b c) /-- info: a ∧ b ∧ c ∧ d ∧ e : Prop -/ #guard_msgs in #check assoc_and% ((a b) c) (d e) end Test end Macro universe u -- Similar to Haskell NonEmpty. Does this exist in Lean stdlib? structure NonEmptyList (α : Type u) where head : α tail : List α instance {α : Type u} : Append (NonEmptyList α) where append l1 l2 := l1.head, l1.tail ++ l2.head :: l2.tail namespace Expr open Lean Meta Elab Qq def collectArgs : Expr NonEmptyList Expr | .app (.app (.const ``And _) a) b => collectArgs a ++ collectArgs b | e => e, [] def rightAssoc (head : Expr) : List Expr Expr | [] => head | x :: xs => mkApp2 (mkConst ``And) head (rightAssoc x xs) def assocAndMeta (e : Expr) : Expr := let args := collectArgs e rightAssoc args.head args.tail /-- An elab version of the expression transform -/ elab "assoc_and_expr% " t:term : term => do let expr Term.elabTerm t none assocAndMeta expr |> pure section Test variable (a b c d e: Prop) /-- info: a : Prop -/ #guard_msgs in #check assoc_and_expr% a /-- info: a : Prop -/ #guard_msgs in #check assoc_and_expr% (a) /-- info: a ∧ b ∧ c : Prop -/ #guard_msgs in #check assoc_and_expr% ((a b) c) /-- info: a ∧ b ∧ c : Prop -/ #guard_msgs in #check assoc_and_expr% (a b) c /-- info: a ∧ b ∧ c : Prop -/ #guard_msgs in #check assoc_and_expr% a (b c) /-- info: a ∧ b ∧ c ∧ d ∧ e : Prop -/ #guard_msgs in #check assoc_and_expr% ((a b) c) (d e) end Test end Expr namespace MetaMQq open Lean Meta Elab Qq def rightAssocQ (head: Q(Prop)) : List Q(Prop) MetaM Q(Prop) | [] => head |> pure | x :: xs => do let tail rightAssocQ x xs q($head $tail) |> pure partial def collectArgsQ : Q(Prop) MetaM (NonEmptyList Q(Prop)) | ~q($a $b) => return ( collectArgsQ a) ++ ( collectArgsQ b) | ~q($e) => return e, [] def assocAndMetaQ (e : Expr) : MetaM Expr := do let 1, ~q(Prop), e inferTypeQ e | throwError m!"{e} is not a Prop" let args collectArgsQ e rightAssocQ args.head args.tail /-- A Qq version of the expression transform -/ elab "assoc_and_qq% " t:term : term => do let expr Term.elabTerm t none assocAndMetaQ expr

1.1.1. Tests🔗

section Test variable (a b c d e: Prop) a : Prop#check assoc_and_qq% a
a : Prop
a : Prop#check assoc_and_qq% (a)
a : Prop
a b c : Prop#check assoc_and_qq% ((a b) c)
a  b  c : Prop
a b c : Prop#check assoc_and_qq% (a b) c
a  b  c : Prop
a b c : Prop#check assoc_and_qq% a (b c)
a  b  c : Prop
a b c d e : Prop#check assoc_and_qq% ((a b) c) (d e)
a  b  c  d  e : Prop
#check 1 is not a Propassoc_and_qq% 1
1 is not a Prop
end Test end MetaMQq end AndAssoc