Zulip Chat Archive

Stream: general

Topic: programming in lean


Chris Hughes (Jul 11 2018 at 11:20):

The following code from Chapter 9 of Programming in Lean doesn't work in lean-nightly-2018-04-20

meta def normalize_hyp (lemmas : list expr) (hyp : expr) : tactic unit :=
do try (simp_at hyp lemmas)

meta def normalize_hyps : tactic unit :=
do hyps  local_context,
lemmas  monad.mapm mk_const [``iff_iff_implies_and_implies,
``implies_iff_not_or, ``not_and_iff, ``not_or_iff, ``not_not_iff,
``not_true_iff, ``not_false_iff],
monad.for' hyps (normalize_hyp lemmas)

Can someone help me get it working??

Simon Hudon (Jul 11 2018 at 11:49):

You can rewrite the first function as:

meta def normalize_hyp (lemmas : list expr) (hyp : expr) : tactic unit :=
do try (simp_hyp lemmas [] hyp)

Simon Hudon (Jul 11 2018 at 11:50):

For the second function, this should work:

meta def normalize_hyps : tactic unit :=
do hyps  local_context,
lemmas  list.mmap mk_const [``iff_iff_implies_and_implies,
``implies_iff_not_or, ``not_and_iff, ``not_or_iff, ``not_not_iff,
``not_true_iff, ``not_false_iff],
hyps.mmap' (normalize_hyp lemmas)

Chris Hughes (Jul 11 2018 at 11:52):

It doesn't quite work. It complains lemmas has type list expr but needs to have type simp_lemmas

Chris Hughes (Jul 11 2018 at 11:53):

I'm a bit confused about the type simp_lemmas. It's defined as a constant, which seems a bit odd to me.

Mario Carneiro (Jul 11 2018 at 11:54):

it's a special cached representation of a simp set

Mario Carneiro (Jul 11 2018 at 11:54):

you should use the simp_lemmas functions to construct one

Chris Hughes (Jul 11 2018 at 11:55):

So it looks almost like an inductive type, but they made all the constructors constants for some reason.

Chris Hughes (Jul 11 2018 at 11:55):

I'll see if I can fix the list myself.

Simon Hudon (Jul 11 2018 at 11:56):

simp_lemmas.append and simp_lemmas.mk should be sufficient

Simon Hudon (Jul 11 2018 at 11:57):

meta def normalize_hyps : tactic unit :=
do hyps  local_context,
lemmas  list.mmap mk_const [``iff_iff_implies_and_implies,
``implies_iff_not_or, ``not_and_iff, ``not_or_iff, ``not_not_iff,
``not_true_iff, ``not_false_iff],
simp_lemmas.append simp_lemmas.mk lemmas,
hyps.mmap' (normalize_hyp lemmas)

Simon Hudon (Jul 11 2018 at 11:57):

... and adjust normalize_hyp accordingly ;-)

Kevin Buzzard (Jul 11 2018 at 12:11):

Yesterday we found something else in PIL which didn't work any more (I think the first line was open state and already that failed IIRC). Maybe Chris and I should collect up some updates for Lean 3.4.1 and submit a PR. Would that sort of thing be welcome?

Chris Hughes (Jul 11 2018 at 12:15):

How does the notation with the backticks and brackets work? What's the difference between 'x, '''x, '''x, '(x), ''(x) and '''(x)`?

Mario Carneiro (Jul 11 2018 at 12:35):

  • `my.name is the way to refer to a name. It is essentially a form of string quoting; no checks are done besides parsing dots into namespaced names
  • ``some does name resolution at parse time, so it expands to `option.some and will error if the given name doesn't exist
  • `(my expr) constructs an expression at parse time, resolving what it can in the current (of the tactic) namespace
  • ``(my pexpr) constructs a pre-expression at parse time, resolving in the current (of the tactic) namespace
  • ```(my pexpr) constructs a pexpr, but defers resolution to run time (of the tactic), meaning that any references will be resolved in the namespace of the begin end block of the user, rather than the tactic itself

Chris Hughes (Jul 11 2018 at 12:53):

So can I give ''(my pexpr) when an expr is expected and it will elaborate it into an expr at parse time?

Simon Hudon (Jul 11 2018 at 12:58):

About back ticks and quoting them: you can include them `` `(foo) `` by using one more backtick to quote what's inside

Simon Hudon (Jul 11 2018 at 12:59):

And I produced the above by writing ``` `` `(foo) `` ```

Mario Carneiro (Jul 11 2018 at 13:01):

If an expr is expected, then you should either use `(my expr) to construct one, or to_expr ``(my pexpr) or one of its variants to perform elaboration (but not name resolution) at run time

Sebastien Gouezel (Jul 11 2018 at 13:47):

I see you are starting a very useful cheat sheet on backticks. Maybe you could add a word on `[apply %%h]? (as far as I can tell, it is not mentioned in Programming in Lean)

Mario Carneiro (Jul 11 2018 at 13:51):

`[tac...] is exactly the same as begin tac... end in the sense that it parses tac... using the interactive mode parser, but instead of evaluating the tactic to produce a term, it just wraps up the list of tactics as a single tactic of type tactic unit. This is useful for writing "macros" or light-weight tactic writing

Chris Hughes (Jul 11 2018 at 13:52):

What is %%? I couldn't find it in programming in lean.

Mario Carneiro (Jul 11 2018 at 13:53):

Re %%: This is called anti-quotation, and is supported in all the expr and pexpr quoting expressions `(expr), ``(pexpr), ```(pexpr), as well as `[tacs]. Wherever an expression is expected inside one of these quoting constructs, you can use %%e instead, where e has type expr in the outer context of the tactic, and it will be spliced into the constructed expr/pexpr/etc.

Chris Hughes (Jul 11 2018 at 13:54):

are ` and %% notation for some function or some deeper inbuilt thing?

Mario Carneiro (Jul 11 2018 at 13:54):

a bit of both

Mario Carneiro (Jul 11 2018 at 13:55):

It is difficult to completely replicate the behavior of the quoting expressions, but %% is supported through pexpr.subst if I recall correctly

Chris Hughes (Jul 11 2018 at 13:55):

so %% turns an expr into the thing that the expr represents, like a real or a nat or something?

Mario Carneiro (Jul 11 2018 at 13:56):

You can always just write it down and then inspect the resulting tactic with #print to find out how the sausage is made

Mario Carneiro (Jul 11 2018 at 13:56):

No, %% inserts the expr into a hole in another expr you are creating

Mario Carneiro (Jul 11 2018 at 13:57):

For example, if a b : expr then `(%%a + %%b) is of type expr

Mario Carneiro (Jul 11 2018 at 13:58):

Probably a and b are expressions for something of type nat, say, and then the resulting expr will also be a valid expression for a nat

Mario Carneiro (Jul 11 2018 at 14:01):

Also worth mentioning are expr pattern matches, which have the same syntax like `(%%a + %%b). These can be used in the pattern position of a match or on the left side of a <- in do notation, and will destruct an expression and bind the antiquoted variables

Mario Carneiro (Jul 11 2018 at 14:03):

For example, if e is an expression then do `(%%a = %%b) <- return e, ... will check that e is an equality, and bind the LHS and RHS to a and b (of type expr), and if it is not an equality the tactic will fail

Chris Hughes (Jul 11 2018 at 14:11):

so when you say `(my expr) , my expr is not an object of type expr it's an actual expression like `(2 + 2)

Mario Carneiro (Jul 11 2018 at 14:14):

right

Chris Hughes (Jul 11 2018 at 16:26):

How do tactics that take arguments work? I'm trying to write the easiest tactic I could think of, which basically does apply quotient.induction_on, intros I'm not sure what the type of the argument to the tactic should be, or even if it should be an argument. I couldn't find anything in PIL on this.

import tactic.interactive data.multiset

meta def qcases (x : pexpr) : tactic unit :=
`[apply quotient.induction_on %%x, intro]

example (x : multiset ) : x = sorry :=
begin
  qcases x,
end

Sebastian Ullrich (Jul 11 2018 at 16:43):

See section 6 of https://leanprover.github.io/papers/tactic.pdf; this is the best source for tactic writing, because it's the only one

Chris Hughes (Jul 11 2018 at 16:52):

Thanks.

Chris Hughes (Jul 13 2018 at 15:39):

One thing I've noticed is that putting tactics in the tactic.interactive namespace changes their behaviour. For example the following code.
Should all tactics be in the tactic.interactive namespace? What difference does this namespace make to their behaviour?

open tactic tactic.interactive interactive.types interactive lean.parser

meta def skip2 (p : parse texpr) : tactic unit :=
do `[skip]

meta def skip3 : tactic unit :=
do `[skip]

namespace tactic.interactive

meta def skip4 (p : parse texpr) : tactic unit :=
do `[skip]

end tactic.interactive

example (s : ) : s = sorry :=
begin
  skip4 s, --works
  skip3,   --works
  skip2 s, --fails
end

Sebastian Ullrich (Jul 13 2018 at 15:45):

Also section 6 :)

Kevin Buzzard (Jul 13 2018 at 17:19):

The interactive namespace is where the tactics which you use in tactic mode live. Is there a difference between by x and begin x end?

Simon Hudon (Jul 13 2018 at 17:33):

I think officially, begin x end is shorthand for by { x }

Chris Hughes (Jul 14 2018 at 09:21):

So if I look at the definition of monad, and some other definitions in tactic.interactive, there are := in places I haven't seen them before. What does it mean to put a := in a field of a structure like this? Same question for the cfg argument of dunfold.

class monad (m : Type u  Type v) extends applicative m, has_bind m : Type (max (u+1) v) :=
(map := λ α β f x, x >>= pure  f)
(seq := λ α β f x, f >>= (<$> x))

meta def dunfold (cs : parse ident*) (l : parse location) (cfg : dunfold_config := {}) : tactic unit :=

Patrick Massot (Jul 14 2018 at 09:27):

Aren't they default values for these fields?

Chris Hughes (Jul 14 2018 at 09:38):

I thought that's what they probably were, but I wasn't sure.

Patrick Massot (Jul 14 2018 at 09:39):

see https://leanprover.github.io/reference/declarations.html#structures-and-records

Patrick Massot (Jul 14 2018 at 09:40):

Lean also allows you to specify a default value for any field in a structure by writing (fieldᵢ : βᵢ := t).

Kevin Buzzard (Jul 14 2018 at 11:04):

Here's an example from core lean (init/algebra/order):

class preorder (α : Type u) extends has_le α, has_lt α :=
(le_refl :  a : α, a  a)
(le_trans :  a b c : α, a  b  b  c  a  c)
(lt := λ a b, a  b  ¬ b  a)
(lt_iff_le_not_le :  a b : α, a < b  (a  b  ¬ b  a) . order_laws_tac)

I guess it means you can either skip the definition of lt or decide to over-write

Chris Hughes (Jul 14 2018 at 12:04):

Another thing that's very odd about the meta code is the use of the constant keyword everywhere. If I look at the type simp_lemmas for example, it's almost like an inductive type, but all the constructors are constants. This just seems totally unworkable to me, because there aren't any eliminators. Is this because this stuff interfaces with C++ code? Or is this some weird case of the programming with expressions rather than data, where defeq terms can give different behaviour if they are syntactically different.

meta constant simp_lemmas : Type
meta constant simp_lemmas.mk : simp_lemmas
meta constant simp_lemmas.join : simp_lemmas  simp_lemmas  simp_lemmas
meta constant simp_lemmas.erase : simp_lemmas  list name  simp_lemmas
meta constant simp_lemmas.mk_default : tactic simp_lemmas
meta constant simp_lemmas.add : simp_lemmas  expr  tactic simp_lemmas
meta constant simp_lemmas.add_simp : simp_lemmas  name  tactic simp_lemmas
meta constant simp_lemmas.add_congr : simp_lemmas  name  tactic simp_lemmas

Mario Carneiro (Jul 14 2018 at 12:20):

It's not an inductive type

Mario Carneiro (Jul 14 2018 at 12:20):

that's why there are no eliminators

Mario Carneiro (Jul 14 2018 at 12:21):

The reason it's all meta constants is, as you say, that it is interfacing with C++

Mario Carneiro (Jul 14 2018 at 12:21):

these are all just hooks to C++ functions

Mario Carneiro (Jul 14 2018 at 12:26):

If by "eliminator" you mean "a way to use this simp_lemmas thing once I've built one", that would be in tactics like ext_simplify_core and such that accept a simp_lemmas object to simp with

Kenny Lau (Feb 08 2019 at 22:45):

In programming in lean the "try it yourself" buttons doesn't really work; the Lean core library seems to be non-existent


Last updated: Dec 20 2023 at 11:08 UTC