Zulip Chat Archive

Stream: lean4

Topic: defeq sort and reassociation


Tomas Skrivan (Sep 27 2022 at 18:15):

I have a simple inductive type for expression with a single binary operation. I would like to write a function that reassociates and orders all terms in the expression. The trouble is that I want this function to work well with defeq.

The expression type:

inductive Expr where
| val (a : Nat) : Expr
| op (x y : Expr) : Expr

I want function def Expr.sort : Expr → Expr such that

example : (val 3 * (val 2 * val 1)).sort = val 1 * val 2 * val 3 := by rfl   -- The `by rfl` is important!

My attempt:
What is the problem with the Expr.sort function such that is causes troubl to rfl tacitc?

inductive Expr where
| val (a : Nat) : Expr
| op (x y : Expr) : Expr
deriving Inhabited

export Expr (val)

def Expr.sort : Expr  Expr
| val a => val a
| op x (val b) =>
  match x.sort with
  | val a =>
    if a  b
    then op (val a) (val b)
    else op (val b) (val a)
  | op x (val a) =>
    if a  b
    then op (op x (val a)) (val b)
    else op (op x (val b)).sort (val a)
  | _ => unreachable!
| op x (op y z) => (op (op x y) z).sort
decreasing_by sorry

instance : Mul Expr := λ x y => x.op y

def Expr.toString : Expr  String
| val a => s!"{a}"
| op x y => s!"({x.toString} * {y.toString})"
instance : ToString Expr := λ e => e.toString

-- check if it is working
#eval (val 3 * (val 2 * val 1)).sort

-- swapping works
example : (val 3 * val 2).sort = val 2 * val 3 := by rfl
example : (val 1 * val 3 * val 2).sort = val 1 * val 2 * val 3 := by rfl

-- left association does not work
example : (val 1 * (val 2 * val 3)).sort = val 1 * val 2 * val 3 := by rfl

Tomas Skrivan (Sep 27 2022 at 18:16):

I can do it using bubble sort on List but I would like to understand what is wrong with the function Expr.sort above

def List.bubblesort [LT α] [DecidableRel (. < . : α  α  Prop)] (l : List α) : {l' : List α // l.length = l'.length} :=
  match l with
  | [] => ⟨[], rfl
  | x :: xs =>
    match bubblesort xs with
    | ⟨[], h => ⟨[x], by simp[h]⟩
    | y :: ys, h =>
      if y < x then
        have : Nat.succ (length ys) < Nat.succ (length xs) := by rw [h, List.length_cons]; apply Nat.lt_succ_self
        let zs, he := bubblesort (x :: ys)
        y :: zs, by simp[h,  he]⟩
      else
        x :: y :: ys, by simp[h]⟩
termination_by _ => l.length

def Expr.vals : Expr  List Nat
| val a => [a]
| op x y => x.vals.append y.vals

def Expr.fromVals : List Nat  Expr
| [] => val 0
| [x] => val x
| x :: xs => op (fromVals xs) (val x)

def Expr.sort' (e : Expr) : Expr := Expr.fromVals e.vals.bubblesort.1.reverse


-- It works now!
example : (val 3 * (val 2 * val 1)).sort' = val 1 * val 2 * val 3 := by rfl

Tomas Skrivan (Sep 27 2022 at 18:24):

In my code I'm actually interested in doing this for expressions with one binary and one unary operations.

inductive Expr where
| val (a : Nat) : Expr
| foo (x : Expr) : Expr
| op  (x y : Expr) : Expr

But then one needs to define a bit more complicated ordering to define.

Effectively, I'm interested in finding a unique representative for each expression under assumption what op is commutative and associative operation and the function returning this representative should respect defeq.


Last updated: Dec 20 2023 at 11:08 UTC