Zulip Chat Archive

Stream: metaprogramming / tactics

Topic: filtering an expr list for matches


Robert Maxton (Jun 07 2022 at 16:06):

So, just getting my toes wet with meta here...

Let's say I have a list of expressions, and I would like to partition them into a sublist that matches a given pattern and a sublist that does not. What's the easiest way to do this? There's docs#tactic.match_expr but a) that's a tactic and b) there's a weird argument I don't understand (what exactly does it mean a function takes an argument that's already defined, anyway?); there's also docs#expr.match_app (for example) but it also takes a fixed argument, in this case elab.

Robert Maxton (Jun 07 2022 at 16:06):

which I don't know how to provide

Kevin Buzzard (Jun 07 2022 at 16:09):

The := in the function input is a default value:

def add (n : ) (m :  := 2) :  := n + m

#eval add 5 -- 7
#eval add 5 3 -- 8

Alex J. Best (Jun 07 2022 at 16:12):

Can you give a bit more detail about what sort of thing you want to match, or even some code with blanks to be filled in?

Damiano Testa (Jun 07 2022 at 18:48):

This may not be the most efficient code, but, given what I imagine that you want to do with the smul application, it might be useful.

import tactic.core

namespace tactic

/-- Sort a list of `expr`s into two lists: one consisting of expressions that begin with an `expr.app` and one consisting of the rest. -/
meta def select_apps : list expr  list expr × list expr
| [] := ([], [])
| (e@(expr.app _ _) :: as) := let (l, r) := select_apps as in  -- the `e@` assigns the name `e` to the matched expression, so you can use it later.
  (e :: l, r)
| (e :: as) := let (l, r) := select_apps as in
  (l, e :: r)

example : 5 + 4 = 0  (let k :  := 1 in 3 * k = 3)  ite (4 = 5) 7 8 = 0  ( n : , n = 0) :=
by do  -- <--- put the cursor here to see the result of the `trace`s
  `(%%e1  %%e2  %%e3  %%e4)  target,
  let (apps, napps) := select_apps [e1, e2, e3, e4],
  trace "* applications:",
  trace apps,
  trace "\n* non applications:",
  trace napps

end tactic

I think that this code contains most of the tricks that I constantly use when meta-programming.

Robert Maxton (Jun 07 2022 at 23:33):

Alex J. Best said:

Can you give a bit more detail about what sort of thing you want to match, or even some code with blanks to be filled in?

Well, I'd like to be able to match any pattern, really; but specifically I want to find things of the form ( _ • r) for a constant r

Robert Maxton (Jun 07 2022 at 23:34):

Damiano Testa said:

This may not be the most efficient code, but, given what I imagine that you want to do with the smul application, it might be useful.
(snip)
I think that this code contains most of the tricks that I constantly use when meta-programming.

Yeah, I was wondering if I would have to just write my own inductive destructor. Thanks!

Robert Maxton (Jun 07 2022 at 23:49):

Chasing both approaches at once, can anyone guess what's going wrong here?

meta def tactic.interactive.multiset_smul_collect : conv unit :=
  do {
    (_, lhs, rhs)  tactic.target_lhs_rhs,
    match expr.to_list (λ e, some e) lhs with
    | some (hd :: tl) :=
          do {
            `(%%a  %%r)  return hd,
            tactic.trace (list.filter_map (``(_  %%r)).match_app tl )
          }
    | _ := tactic.trace "oops"
    end
    },
    return

It's not intended to really do anything yet (other than trace), but there's an error under list.filter_map complaining about a type mismatch:

type mismatch at application
  list.filter_map (``(λ (_x_1 : _), has_scalar.smul _ _x_1).subst (to_pexpr r)).match_app
term
  (``(λ (_x_1 : _), has_scalar.smul _ _x_1).subst (to_pexpr r)).match_app
has type
  option (expr ff × expr ff) : Type
but is expected to have type
  ?m_1  option ?m_2 : Type (max ? ?)

Robert Maxton (Jun 07 2022 at 23:50):

which I don't know what to make of

Robert Maxton (Jun 07 2022 at 23:53):

Oh, wait, dammit I got my wires crossed and thought I was working with the generic match-a-pattern functionality from tactics. Nevermind lol

Damiano Testa (Jun 08 2022 at 01:54):

If I understood correctly, this might be useful:

import tactic.core
import algebra.group.defs

namespace expr

--  this function appears in `move_add`
meta def list_summands : expr  list expr
| `(%%a + %%b) := a.list_summands ++ b.list_summands
| a            := [a]

--  similar to function `candidates` in `move_add`
meta def list_smuls : expr  list (expr × expr)
| `(%%a  %%r)   := prod.mk <$> a.list_summands <*> r.list_summands
| (lam _ _ e f)  := e.list_smuls ++ f.list_smuls
| (pi  _ _ e f)  := e.list_smuls ++ f.list_smuls
| (mvar  _ _ e)  := e.list_smuls
| (app e f)      := e.list_smuls ++ f.list_smuls
| (elet _ e f g) := e.list_smuls ++ f.list_smuls ++ g.list_smuls
| e              := []

end expr

variables {R M : Type*} [has_add R] [has_mul R] [has_add M] [has_scalar R M]
  (a b c d e f : R) (p q r s t : M)

example :  x : ,  let k := d  t in -- `∀` adds useless noise, the `let` contributes below
  (a + b * c)  p + (c + (d + e) * f)  (q + r) = (a + (b + c) + d + (e + f))  s + t :=
by do  --  note that the isolated `t` on the rhs does not appear in the list :(
  t  tactic.target,
  tactic.trace t.list_smuls

Robert Maxton (Jun 10 2022 at 07:33):

Damiano Testa said:

If I understood correctly, this might be useful:
snip

hmm, this does look useful
thanks, I was actually just about to ask about a related issue
let's see if this pattern works


Last updated: Dec 20 2023 at 11:08 UTC