-
Notifications
You must be signed in to change notification settings - Fork 26
feat: Simp, made simple #98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: Simp, made simple #98
Conversation
5ed721a to
2feca6c
Compare
This PR adds the first blog post in a series of three to introduce the concept of simprocs and explain how to write them. The second blog post can be found here: #98 Co-authored-by : Yaël Dillies <[email protected]>
|
Part 1 references this post, so maybe it can also be updated to have working links pointing here too. |
posts/simp-made-simple.md
Outdated
| ``` | ||
| Note: The above snippet is a simplification and the constructors as shown actually belong to `Lean.TransformStep`, which `Lean.Meta.Simp.DStep` is an `abbrev` of. | ||
|
|
||
| <span style="color:red">**(Yaël): Why is there a mismatch in docstrings between `Step.continue` and `DStep.continue`? [Zulip](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Simp.2EStep.2Econtinue.20vs.20Simp.2EDStep.2Econtinue/with/509056271)**</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to remove this?
ed3714e to
04f3d50
Compare
Co-authored-by: Paul Lezeau <[email protected]>
Co-authored-by: Eric Wieser <[email protected]>
Co-authored-by: Bhavik Mehta <[email protected]>
Co-authored-by: Bhavik Mehta <[email protected]>
04f3d50 to
f7aa206
Compare
edegeltje
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thx for writing this
Co-authored-by: blizzard_inc <[email protected]>
posts/simp-made-simple.md
Outdated
| ``` | ||
|
|
||
| `simp` does not consume bare elements of type `Simproc`. | ||
| Instead, a simproc is an element of type `Simproc` annotated with tbe extra data mentioned in the overview subsection, like whether the simproc is `pre` or `post`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The section on Step is great, and contains a lot of detail. These latter sections seem somewhat rushed and the ending of the blogpost feels abrupt to me.
For example, this sentence about annotations with extra data... could that be accompanied by a code block that shows exactly what is going on?
And would it make sense to give the exact definition of the simpM monad, and add a bit more detail on it?
Maybe you could even end with a little cliff hanger about what will happen in the final blogpost?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good! I'm planning on spending some extra time adding stuff about SimpM to the post this coming month.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🏓 @Paul-Lez and @YaelDillies, it would be great to get this out! :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kim-em this is now ready to review if you fancy taking a look;)
If you're very busy, I think the first part is probably more ready/polished than the second one!
Co-authored-by: Yaël Dillies <[email protected]>
Co-authored-by: Johan Commelin <[email protected]>
Co-authored-by: Paul Lezeau <[email protected]>
Co-authored-by: Yaël Dillies <[email protected]>
206fa34 to
3ac5a4c
Compare
posts/simp-made-simple.md
Outdated
| - `continue`. | ||
| It turns out that both `Nat.reduceDvd` and `reduceIte` also use the `continue` constructor. | ||
| Indeed, both these simprocs rely on the fact that some part of the expression | ||
| considered is simplifiable (e.g. the condition in the `if` statement). | ||
| When this is not the case, the `continue` constructor signals to `simp` that it should | ||
| *not* attempt to simplify the expression again using the same simproc to prevent | ||
| the simplification procedure from looping around. | ||
| More generally, `continue` is used in most simprocs as the "default" output | ||
| produced when the simproc was not able to make any simplification. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't completely understand this explanation, it seems like there are two things being mixed up here. Step indicates what to do when the simproc is done, right? But here "some part of the expression considered is simplifiable (e.g. the condition in the if statement)." refers to simplifying before the logic of the simproc itself. And then you describe when not to use .continue instead of when you do want to use it. Also because the .visit/.continue distinction is only relevant for pre-simprocs, which is mentioned above, but not here.
If we want to rework this completely, I would do something like: open the list with .continue since .continue none is the default. Then say something like:
...
.continueindicates that the simproc is done with this expression and its subexpressions. As a result,simpwill not not attempt to simplify the expression again using the same simproc to prevent the simplification procedure from looping around.
posts/simp-made-simple.md
Outdated
|
|
||
| In the case where the two expressions `e` and `e'` are definitionally equal, | ||
| one can actually describe a simplification step using a simple structure, | ||
| namely `DStep` (where the "d" stands for "definitional"). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DSimprocs are mentioned only later on. So it is weird to discuss DStep here, it will make the reader wonder how they can return a DStep instead of a Step from a simproc.
posts/simp-made-simple.md
Outdated
| 3) The second monad transformer application: `ReaderT Simp.Context $ StateRefT Simp.State MetaM`. | ||
| The `SimpM` monad should also be able to access the "context" that `simp` is running in, e.g. which simp theorems it has access to and so on. This is captured by the type `Simp.Context`. | ||
| Here, the situation is not quite the same as when we were adding a `Simp.State` state to `MetaM`: while we will often want to change the state during the `simp` call, the context should always be the same (in programmer lingo: _immutable_) | ||
| Thus, we use a different monad transformer called `ReaderT`, which is almost identical to `StateT`, but outputs a new monad that can only read the type passed as parameter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mildly pedantic, but ReaderT can change the value in the calls it makes (e.g. ReaderT.adapt as equivalent to modify for a Reader). In programming language nerd terms, State(Ref)T gives you a global variable, and ReaderT gives you dynamic scoping.
(Does simp in practice use that though?)
Vierkantor
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few places where things are not clear to me, but you did mention everything I was expecting to see. Did not yet read it closely for style.
YaelDillies
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first 140 lines flow really well! Let's make sure this is also the case for the other 200 lines after that.
| 2. Traverse subexpressions of `e` (note that the preprocedures might have changed `e` by this point); | ||
| 3. Run postprocedures on `e`. | ||
|
|
||
| We call it the *simplification loop*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| We call it the *simplification loop*. | |
| We call this the *simplification loop*. |
| Both `visit` and `done` signify success. | ||
|
|
||
| Whenever a simproc is called on a given expression, it outputs a `Step`, which determines what will happen next during the `simp` call. | ||
| Since every simproc call is running a metaprogram to produce the output `Step`, the constructor that ends up used may vary according to the input. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Since every simproc call is running a metaprogram to produce the output `Step`, the constructor that ends up used may vary according to the input. | |
| Since every simproc call is running a metaprogram to produce the output `Step`, the constructor that ends being up used may vary according to the input. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Since every simproc call is running a metaprogram to produce the output `Step`, the constructor that ends up used may vary according to the input. | |
| Since every simproc call is running a metaprogram to produce the output `Step`, the constructor that ends up being used may vary according to the input. |
|
|
||
| Whenever a simproc is called on a given expression, it outputs a `Step`, which determines what will happen next during the `simp` call. | ||
| Since every simproc call is running a metaprogram to produce the output `Step`, the constructor that ends up used may vary according to the input. | ||
| For example, a given simproc may in some cases use `visit` and in others use `continue`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence is quite strange to me: it bases itself off the assumption that people expect that a simproc always uses the same Step constructor, but four lines above we explain how to use the constructors according to whether the simproc has failed or not. Could you repackage the info in a way that acknowledges the previous sentence?
| The constructors do the following: | ||
| - `continue` indicates that the simproc is done with this expression. | ||
| As a result, simp will not not attempt to simplify the expression again using the same simproc to prevent the simplification procedure from looping around. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| As a result, simp will not not attempt to simplify the expression again using the same simproc to prevent the simplification procedure from looping around. | |
| As a result, simp will not attempt to simplify the expression again using the same simproc to prevent the simplification procedure from looping. |
| Recall from the first post the simproc `Nat.reduceDvd`. | ||
| This takes expressions of the form `a | b` where `a`, `b` are explicit natural numbers, and returns `True` or `False`. | ||
| Either way, the output is in simp normal form, so there is no need to attempt to simplify it further. | ||
| Thus, the simproc uses `done` to this result. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You already recalled on L.156
| Recall from the first post the simproc `Nat.reduceDvd`. | |
| This takes expressions of the form `a | b` where `a`, `b` are explicit natural numbers, and returns `True` or `False`. | |
| Either way, the output is in simp normal form, so there is no need to attempt to simplify it further. | |
| Thus, the simproc uses `done` to this result. | |
| When `Nat.reduceDvd` is called on an expression of the form `a | b` where `a`, `b` are explicit natural numbers, it simplifies it to `True` or `False`. | |
| Either way, the output is in simp normal form and there is no need to simplify it further. | |
| Thus `Nat.reducedDvd` uses `done` in such a case. |
| ## Exploring the `SimpM` monad via simprocs | ||
|
|
||
| In the next blog post, we will cover in detail how to implement simprocs that are useful for proving theorems in Lean. | ||
| In the meantime, to whet the reader's appetite, let's have a go at exploring the `SimpM` monad's internals using simprocs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| In the meantime, to whet the reader's appetite, let's have a go at exploring the `SimpM` monad's internals using simprocs. | |
| In the meantime, to whet the reader's appetite, let's explore the internals of the `SimpM` monad using fake simprocs that print info instead of simplifying. |
| In the next blog post, we will cover in detail how to implement simprocs that are useful for proving theorems in Lean. | ||
| In the meantime, to whet the reader's appetite, let's have a go at exploring the `SimpM` monad's internals using simprocs. | ||
|
|
||
| > Throughout this section, all code will be assumed to have `open Lean Elab Meta Simp`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| > Throughout this section, all code will be assumed to have `open Lean Elab Meta Simp`. | |
| > Throughout this section, all code is assumed to be prefaced with `open Lean Elab Meta Simp`. |
| -- declare the simproc | ||
| simproc_decl printExpr (_) := printExpressions | ||
| ``` | ||
| The last line is needed to "declare" the simproc officially - this is where we specify information like whether this is a pre/post procedure, and what expression we're matching on (here, we match on the pattern `_`, i.e. on everything!). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| The last line is needed to "declare" the simproc officially - this is where we specify information like whether this is a pre/post procedure, and what expression we're matching on (here, we match on the pattern `_`, i.e. on everything!). | |
| The last line is needed to "declare" the simproc officially - this is where we can specify priority, whether this is a pre/post procedure and what expression we're matching on (here, we match on the pattern `_`, i.e. on everything!). |
We didn't mention the pattern on L.46-47. Should we?
| The last line is needed to "declare" the simproc officially - this is where we specify information like whether this is a pre/post procedure, and what expression we're matching on (here, we match on the pattern `_`, i.e. on everything!). | ||
| More on this in the next post. | ||
|
|
||
| Next, we could try an print out theorems that have used by `simp` "so far". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Next, we could try an print out theorems that have used by `simp` "so far". | |
| Next, let's print out all the theorems that have been used by `simp` "so far". |
| simproc_decl printThms (_) := printUsedTheorems | ||
| ``` | ||
|
|
||
| The reader can then call add these simprocs to simp calls to see what's happening within. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| The reader can then call add these simprocs to simp calls to see what's happening within. | |
| We encourage the reader to add these simprocs to their simp calls to see what's happening within. |
This is the second part of #97.
Co-authored-by: Paul Lezeau [email protected]