Zulip Chat Archive
Stream: lean4
Topic: Lexicographical LinearOrder
Marcus Rossel (Apr 03 2023 at 14:10):
What is the easiest way to derive LinearOrder for some structure whose fields all have LinearOrder instances?
E.g. I have:
import Mathlib
def Time := Nat
deriving LinearOrder
structure Tag where
time : Time
microstep : Nat
... and would like to derive:
instance : LinearOrder Tag := ...
... with < being the lexicographical ordering.
I tried deriving LinearOrder on Tag, but that gives:
default handlers have not been implemented yet, class: 'LinearOrder' types: [Tag].
Also, I've seen docs4#Sum.Lex which seems like it might be relevant. But I don't understand how to use it.
Yaël Dillies (Apr 03 2023 at 14:18):
sum.lex won't make a linear order. You probably want psigma.lex instead since all structures can be written as nested sigma types.
Eric Wieser (Apr 03 2023 at 14:40):
cc @Kyle Miller regarding !4#3215 which constructs an equivalence to Sum/Sigma types
Eric Wieser (Apr 03 2023 at 14:40):
I hadn't expected my suggestion there to be needed by someone so quickly!
Kyle Miller (Apr 03 2023 at 18:13):
In mathlib4#3198 I've refactored it to reconfigure how the proxy type is generated.
I think you would just need to make your own mkCtorProxyType by starting with defaultMkCtorProxyType, and make your own term elaborator with this configuration to construct an equivalence.
Kyle Miller (Apr 03 2023 at 18:14):
What's the function to pull a LinearOrder across an Equiv?
Eric Wieser (Apr 03 2023 at 18:14):
Kyle Miller (Apr 03 2023 at 18:20):
import Mathlib.Tactic.ProxyType
import Mathlib.Order.Basic
import Mathlib.Data.Sigma.Order
namespace Mathlib.ProxyType
open Lean Meta Elab Term
def lexMkCtorProxyType (xs : List (Expr × Name)) : TermElabM (Expr × Term) :=
match xs with
| [] => return (mkConst ``Unit, ← `(term| ()))
| [(x, a)] => do
let xty ← inferType x
if ← Meta.isProp xty then
return (← mkAppM ``PLift #[xty], ← `(term| ⟨$(mkIdent a)⟩))
else
return (xty, mkIdent a)
| (x, a) :: xs => do
let (xsty, patt) ← defaultMkCtorProxyType xs
let xty ← inferType x
if ← Meta.isProp xty then
withLocalDeclD `x' (← mkAppM ``PLift #[xty]) fun x' => do
let xsty' := xsty.replaceFVar x (← mkAppM ``PLift.down #[x'])
let ty ← mkAppM ``Lex #[← mkAppM ``Sigma #[← mkLambdaFVars #[x'] xsty']]
return (ty, ← `(term| ⟨⟨$(mkIdent a)⟩, $patt⟩))
else
let ty ← mkAppM ``Lex #[← mkAppM ``Sigma #[← mkLambdaFVars #[x] xsty]]
return (ty, ← `(term| ⟨$(mkIdent a), $patt⟩))
def ProxyEquivConfig.lexDefault (indVal : InductiveVal) : ProxyEquivConfig where
proxyName := indVal.name.mkStr "proxyLexType"
proxyEquivName := indVal.name.mkStr "proxyLexTypeEquiv"
mkCtorProxyType := lexMkCtorProxyType
mkProxyType := defaultMkProxyType
elab "proxy_lex_equiv% " t:term : term <= expectedType => do
let (type, indVal) ← elabProxyEquiv t expectedType
let config : ProxyEquivConfig := ProxyEquivConfig.lexDefault indVal
ensureProxyEquiv config indVal
mkAppOptM config.proxyEquivName (type.getAppArgs.map .some)
end Mathlib.ProxyType
def Time := Nat
deriving LinearOrder
structure Tag where
time : Time
microstep : Nat
instance : LinearOrder Tag :=
LinearOrder.lift' _ (Equiv.injective (Equiv.symm (proxy_lex_equiv% Tag)))
Kyle Miller (Apr 03 2023 at 18:26):
I guess this also needs an implementation of mkProxyType to turn Sum a b into Lex (Sum a b) for multi-constructor inductive types. What's the rationale for Sum not having a LinearOrder by default?
Kyle Miller (Apr 03 2023 at 18:53):
With some more tweaking, this is what it looks like:
import Mathlib.Tactic.ProxyType
import Mathlib.Order.Basic
import Mathlib.Data.Sigma.Order
import Mathlib.Data.Sum.Order
namespace Mathlib.ProxyType
open Lean Meta Elab Term
def ProxyEquivConfig.lexDefault (indVal : InductiveVal) : ProxyEquivConfig where
proxyName := indVal.name.mkStr "proxyLexType"
proxyEquivName := indVal.name.mkStr "proxyLexTypeEquiv"
mkCtorProxyType := defaultMkCtorProxyType (decorateSigma := fun e => mkAppM ``Lex #[e])
mkProxyType := defaultMkProxyType (decorateSum := fun e => mkAppM ``Lex #[e])
syntax (name := proxy_lex_equiv) "proxy_lex_equiv% " term : term
@[term_elab proxy_lex_equiv]
def elab_proxy_lex_equiv : Elab.Term.TermElab := fun stx expectedType? =>
match stx with
| `(proxy_lex_equiv% $t) => do
let (type, indVal) ← elabProxyEquiv t expectedType?
let config : ProxyEquivConfig := ProxyEquivConfig.lexDefault indVal
ensureProxyEquiv config indVal
mkAppOptM config.proxyEquivName (type.getAppArgs.map .some)
| _ => throwUnsupportedSyntax
end Mathlib.ProxyType
def Time := Nat
deriving LinearOrder
structure Tag where
time : Time
microstep : Nat
instance foo : LinearOrder Tag :=
LinearOrder.lift' _ (proxy_lex_equiv% Tag).symm.injective
Kyle Miller (Apr 03 2023 at 18:54):
It uses Lex (Sum _ _) for multi-constructor types:
inductive X
| a : Nat → Nat → X
| b : Nat → X
instance : LinearOrder X :=
LinearOrder.lift' _ (proxy_lex_equiv% X).symm.injective
The type it generates here is Lex (Lex ((_ : ℕ) × ℕ) ⊕ ℕ)
Kyle Miller (Apr 03 2023 at 20:14):
I pushed mathlib4#3251 for this simple LinearOrder derive handler.
Example usage:
import Mathlib.Tactic.DeriveLinearOrder
def Time := Nat
deriving LinearOrder
structure Tag where
time : Time
microstep : Nat
deriving LinearOrder
Kyle Miller (Apr 03 2023 at 20:15):
It supports types more complicated than structures, but not recursive ones or ones with indices.
inductive X (α : Type)
| a : Nat → α → X α
| b : Nat → X α
deriving LinearOrder
#check instLinearOrderX
-- instLinearOrderX {α✝ : Type} [inst✝ : LinearOrder α✝] : LinearOrder (X α✝)
Last updated: May 02 2025 at 03:31 UTC