Zulip Chat Archive

Stream: Is there code for X?

Topic: Qq list


Adam Topaz (Nov 24 2023 at 17:30):

Do we have something which can accomplish something similar to the following?

partial def Qq.list {u : Level} {S : Q(Type u)} (L : Q(List $S)) : MetaM (List Q($S)) := do
  let L : Q(List $S)  whnf L
  match L with
    | ~q([]) => return []
    | ~q($x :: $xs) => return x :: ( Qq.list xs)
    | _ => throwError "Failed"

Eric Wieser (Nov 24 2023 at 17:56):

I doubt it; though we might have the reverse direction

Adam Topaz (Nov 24 2023 at 17:57):

Sure, the reverse direction is more "obvious" i guess.

Eric Wieser (Nov 24 2023 at 17:59):

In general I guess you could generate something like this for any parameterized inductive type?

Adam Topaz (Nov 24 2023 at 17:59):

What are parameterized inductive types?

Adam Topaz (Nov 24 2023 at 18:00):

Oh you mean just an inductive type with parameters.

Adam Topaz (Nov 24 2023 at 18:00):

yes

Adam Topaz (Nov 24 2023 at 18:00):

But such a function would be more complicated, as one would have to obtain the constructors of that type inside of MetaM, etc.

Kyle Miller (Nov 24 2023 at 19:29):

Reflection like this wouldn't work for all inductive types, but it seems like it should for a large class of them (For example, what if your inductive type T has a constructor with a Nat -> T argument?)

By the way, Qq pattern matching does defeq tests, so if you care about efficiency you probably would want to match on constructors directly.

Here's just some messing around, adding a couple more of these functions to be able to evaluate Finsets too.

import Lean
import Qq
import Mathlib.Data.Multiset.Basic
import Mathlib.Data.Fintype.Basic

open Lean Meta Qq

/-- Reduce the spine of a List expression to yield a List of expressions. -/
partial def Qq.list {u : Level} {S : Q(Type u)} (L : Q(List $S)) : MetaM (List Q($S)) := do
  match  whnf L with
    | .app (.const ``List.nil _) _ => return []
    | .app (.app (.app (.const ``List.cons _) _) (x : Q($S))) (xs : Q(List $S)) =>
      return x :: ( Qq.list xs)
    | L' => throwError "Failed to reduce List expression. Got{indentD L'}"

/-- Reduce a Multiset expression to give its underlying List of expressions. -/
partial def Qq.multiset {u : Level} {S : Q(Type u)} (m : Q(Multiset $S)) : MetaM (List Q($S)) := do
  match  whnfD m with
  | .app (.app (.app (.const ``Quot.mk _) _) _) (L : Q(List $S)) => Qq.list L
  | m' => throwError "Failed to reduce Multiset to a `Quot.mk` application. Got{indentD m'}"

/-- Reduce a Multiset expression to give its underlying List of expressions. -/
partial def Qq.finset {u : Level} {S : Q(Type u)} (s : Q(Finset $S)) : MetaM (List Q($S)) := do
  match  whnf s with
  | .app (.app (.app (.const ``Finset.mk _) _) (m : Q(Multiset $S))) _ => Qq.multiset m
  | s' => throwError "Failed to reduce Finset to a constructor application. Got{indentD s'}"

#eval show MetaM Unit from do
  let e : Q(Finset Bool) := q(Finset.univ : Finset Bool)
  let l  Qq.finset e
  dbg_trace "Elements: {l}"
  -- Elements: [Bool.true, Bool.false]

#eval show MetaM Unit from do
  let e : Q(Finset Nat) := q(Finset.range 10 : Finset Nat)
  let l  Qq.finset e
  dbg_trace "Elements: {l}"
  -- Elements: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Kyle Miller (Nov 24 2023 at 19:32):

It would be kind of neat to use a simp-like procedure to do whnf, so that way you can do bigger steps in reduction if you happen to know special properties of a function (or even to be able to reduce things that the kernel can't reduce), while also generating a proof that the result actually represents the original list.

Thomas Murrills (Nov 24 2023 at 20:51):

Btw, I did something like this (the non-Qq List version) way back when for tfae; I’m on mobile but I believe it’s called something like getExplicitList! (It might be missing a whnf though…) If you’re PRing something feel free to factor it out, rename it, and/or improve it. :)

Anne Baanen (Nov 27 2023 at 09:27):

We have the non-recursive matching step as docs#Mathlib.Meta.List.proveNilOrCons. I used a definition like you proposed in the Lean 3 norm_num big operators plugin. I found out that it was nicer to only do the case distinction and handle recursion in the tactic itself.


Last updated: Dec 20 2023 at 11:08 UTC