Skip to content
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

[Ltac2] Add util files for Bool, List, Option #10219

Merged
merged 4 commits into from
Jun 25, 2019

Conversation

JasonGross
Copy link
Member

Most functions are taken either from the Coq standard library or the
OCaml standard library. One (enumerate) is taken from Python. Most
names are chosen according to OCaml conventions (as Coq does not have
exceptions, and so the OCaml naming conventions already distinguish
between option and exception). Since exists is a keyword, we use the
Coq name existsb instead. We generally favor Coq argument ordering
when there is a conflict between Coq and OCaml.

Note that seq matches neither Coq nor OCaml; it takes a step
argument for how much to increment by on each step.

Sorting functions are mostly taken from Coq's mergesort library; it
might make sense to replace them with OCaml's versions for efficiency?

Kind: feature.

Fixes / closes coq/ltac2#19 (mostly; I haven't ported the reimplementation of lazy_match, but I think that's okay)

Which of these should this PR get?

  • Added / updated test-suite

@JasonGross JasonGross added part: standard library The standard library stdlib. kind: enhancement Enhancement to an existing user-facing feature, tactic, etc. part: ltac2 Issues and PRs related to the (in development) Ltac2 tactic langauge. labels May 22, 2019
@JasonGross JasonGross added this to the 8.11+beta1 milestone May 22, 2019

Ltac2 hd (ls : 'a list) :=
match ls with
| [] => Control.zero (Tactic_failure (Some (Message.of_string "hd")))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be discussed in the API guidelines, but I don't think using backtracking exceptions for anything else than proof search is a good idea. I believe that instead panics should be used when preconditions of a function call are not satisfied, which looks like this is the case here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are three sorts of errors, and it is important to be able to express all of them:

  1. Control.zero - failure in the tactic monad. I agree that this is the wrong type of failure for this method.
  2. Catchable exceptions. I think the only catchable exceptions are currently the ones that induce backtracking,* which I think is wrong.
  3. Anomalies (or uncatchable exceptions)

See, for example, #10094 and #10095, where the inability to catch an exception makes it significantly harder to implement a thing.

If you make a version of Control.case that can catch exceptions raised by Control.throw (or else a version of Control.throw whose exceptions can be caught by Control.case), then I'm happy to replace all of the Control.zeros here with Control.throw.

* For example:
Require Import Ltac2.Init.
Require Ltac2.Control.
Require Ltac2.Message.
Goal True.
  match Control.case (fun _ => Control.throw Not_found) with
  | Val a => ()
  | Err e => Message.print (Message.of_exn e)
  end.
  (* Error: Uncaught Ltac2 exception: Not_found *)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My take on that is that we should not reenact the exception hell of OCaml, and separate potential failure from invariant breakage via types. That is, a function that might rightfully "fail" should return options, while a function whose preconditions are violated should return a panic. In OCaml there is a pervasive confusion between these two situations, which leads to catastrophic semantics when trying to scale. (Anomaly: Uncaught exception Not_found might be the single most frequent anomaly raised by Coq for instance.)

There is nothing complicated in doing so, it's just a matter of sticking to a particular style. For instance, your examples could be given as pairs of functions, one to check you are in the right fragment, and one to extract the relevant date assuming the first one says it's OK.

In the case of this PR, hd should fail with a panic. It's your duty to ensure that the list is not empty beforehand, and if you don't know, then just use pattern-matching.

(* \VV/ **************************************************************)
(* // * This file is distributed under the terms of the *)
(* * GNU Lesser General Public License Version 2.1 *)
(************************************************************************)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an outdated header. I wonder where you found it. OMG you found it in the code of Ltac2! I wonder how I could let that split. Anyway, you can find the right header in dev/header.ml

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is dev/header.ml supposed to only extend to 2018, not 2019?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case, I've updated the headers of the new files I added

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn't update any header since 2019 😢 But yes, it is supposed to extend.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there is #10320 now, so you could adapt this PR accordingly.

Most functions are taken either from the Coq standard library or the
OCaml standard library.  One (`enumerate`) is taken from Python.  Most
names are chosen according to OCaml conventions (as Coq does not have
exceptions, and so the OCaml naming conventions already distinguish
between option and exception).  Since `exists` is a keyword, we use the
Coq name `existsb` instead.  We generally favor Coq argument ordering
when there is a conflict between Coq and OCaml.

Note that `seq` matches neither Coq nor OCaml; it takes a `step`
argument for how much to increment by on each step.

Sorting functions are mostly taken from Coq's mergesort library; it
might make sense to replace them with OCaml's versions for efficiency?

Ltac2 ret (x : 'a) := Some x.

Ltac2 lift (f : 'a -> 'b) (x : 'a option) := map f x.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of this alias?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say the name lift is more common when it is partially applied to lift f to 'a option -> 'b option. The name map is more common when it is fully applied. I am usually not a fan of aliases but would say it makes sense in this case because it is more a coincidence that lift and map have the same implementation.

Ltac2 gt (x : int) (y : int) := equal (compare x y) 1.
Ltac2 le (x : int) (y : int) :=
(* we might use [lt x (add y 1)], but that has the wrong behavior on MAX_INT *)
match equal x y with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be more efficient to match on "compare x y" here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ignore, I just learned myself that matching on integers is not yet supported in Ltac2.

@MSoegtropIMC
Copy link
Contributor

@JasonGross: do you agree that most (or probably all) of the Control.zero should be replaced with Control.throw? I agree with @ppedrot that for things like an out of bounds access, a non backtrackable throw would probably help with debugging and getting things right. Also you supply option variants e.g. for hd in case one is not sure. In case you are busy right now, I could also propose a patch for your PR.

@ppedrot : are there any other points which keep this PR from merging? It is a bit hard to work with Ltac2 if basic stuff as defined here is missing.

@JasonGross
Copy link
Member Author

I am pretty busy, please do propose a patch and I will merge. I am mostly sold by the reasoning ppedrot gave, and certainly sold enough to replace all the zeros with throws in this PR

… out of courtesy)

- index errors (negative or out of bounds) generally throw (panic)
- hd, last, find backtrack, because here backtracking can actually be useful
  added a _throw variant to these functions which panics
  This emaphasizes the difference between backtracking and throwing exceptions, which doesn't exist in OCaml.
- simplified the implementation of for_all2 and exist2 (ok, that is a matter of taste ...)
- added assertions which combine a boolean match with a throw.
  This makes the code more readable and makes it easier to have more specific error messages.
- added a lastn function
- gave Out_of_bounds a message argument (there is no good reason why invalid_argument has one and this not)
  All other exceptions without message argument are quite specific to certain functions (like Not_Found)
@MSoegtropIMC
Copy link
Contributor

@JasonGross : I took the freedom to directly push to your PR branch. This makes it easier for others to review my changes in parallel. See the commit message for the changes I did. I hope this is agreeable.

Note on Bool: I find it questionable to use the "b" extension since in Ltac2 there is no differentiation between Prop and bool. I would rather remove this (but didn't do so). The syntax is more like OCaml than like Coq so I would stick to names one would have chosen in OCaml (if these functions wouldn't be operators).

In list I removed the "b" aliases to various exist and for_all functions.

@MSoegtropIMC
Copy link
Contributor

@JasonGross : I take your 👍 as agreement to remove the "b" extensions in Bool.v. I think this would lead to a more regular naming since e.g. the comparison function in Int.v also don't have a "b". I will do this a separate commit.

@MSoegtropIMC
Copy link
Contributor

@ppedrot : would it be possible to merge this PR and later also my PRs (which depend on this)? In my view Ltac2 is very important for Coq and a standard library is very important for Ltac2. We shouldn't stall the good momentum we have right now.

I find @JasonGross approach to follow the API of the OCaml library very agreeable, because it will make it easier to port Ltac-2 to OCaml.

Copy link
Member

@ppedrot ppedrot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree with the current scheme for failure handling. We should have only two variants, not four.

  • A version that uses throw, with the default name (e.g. hd)
  • A version that returns option, with the _opt suffix

Exporting backtrack in standard functions not meant for proof search is another recipe for explosive disaster (in particular as the default function), and default values contaminate the API.

@ppedrot
Copy link
Member

ppedrot commented Jun 19, 2019

@MSoegtropIMC Instead, you should provide option combinators that turn them into backtracking failures and default values accordingly.

@MSoegtropIMC
Copy link
Contributor

@ppedrot : I agree with your comment - less functions and explicit backtracking with an option combinator will most likely help the user the get what (s)he wants. I think my Array.v PR already follows your advice (for array index bound errors it anyway makes no sense to backtrack) but I will review it again with this in mind.

@JasonGross : I will follow @ppedrot advice and reduce the number of options and add the combinators he suggested to Option.v. I hope you agree with this as well.

| Some v => v
| None => Control.zero Not_found
end.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ppedrot : I assume your comment also applies to find above. Here I could imagine that a backtracking Control.zero Not_found might sometimes be what people want, but I guess it is better to make the backtracking always explicit in this kind of library function. Unless I hear from you otherwise, I will change this as well.

@ppedrot
Copy link
Member

ppedrot commented Jun 25, 2019

@MSoegtropIMC sorry for the late review, the request somewhat flew under my radar.

@ppedrot ppedrot self-assigned this Jun 25, 2019
@ppedrot ppedrot merged commit 8137e40 into coq:master Jun 25, 2019
ppedrot added a commit that referenced this pull request Jun 25, 2019
Ack-by: JasonGross
Ack-by: MSoegtropIMC
Ack-by: SkySkimmer
Ack-by: Zimmi48
Reviewed-by: ppedrot
MSoegtropIMC added a commit to MSoegtropIMC/coq that referenced this pull request Jun 26, 2019
@MSoegtropIMC
Copy link
Contributor

@vbgl : would you mind merging this into 8.10? Ltac2 is unusable without such a mini library. The bad thing is that people would start to develop their own incompatible libraries and distribute them.

@MSoegtropIMC
Copy link
Contributor

@vbgl: I just saw that 8.10 has still separate ltac2, so this would get a bit tricky. If you want me to prepare a PR, please let me know.

@vbgl
Copy link
Contributor

vbgl commented Jul 25, 2019

There is no Ltac2 in 8.10 and the legacy repository for Ltac2 has been frozen. So I don’t understand any of your requests. Sorry.

@MSoegtropIMC
Copy link
Contributor

Yes, indeed it would get tricky. Well it is not that bad - since this PR didn't change anything on the ML side people can use the latest Ltac2 .v files and recompile them on their own (that's what I am doing when working with 8.10).

Copy link
Member

Zimmi48 commented Jul 25, 2019

FTR the reason the Ltac2 repository has been frozen is that his author won't maintain it, especially given that most changes to the ML side would require the corresponding changes in Coq's API. However, nothing would prevent us from unfreezing the repository, and let a developer (if one volunteers to do it) backport to v8.10 any Ltac2 change that can be (such as these library files), and then prepare a release.

@MSoegtropIMC
Copy link
Contributor

@Zimmi48 : I would be open to do that if @vbgl as release manager agrees that it makes sense. I think Ltac-2 is an important development which deserves some acceleration.

You know my opinion on Ltac-1: Ltac-1

@vbgl
Copy link
Contributor

vbgl commented Jul 25, 2019

Ltac-2 is already in the CI for the 8.10 branch. If Michael volunteers to maintain the https://github.com/coq/ltac2 repository, it does make sense to unfreeze it, backport valuable contributions and make Ltac-2 for Coq 8.10 a useful plug-in.

@MSoegtropIMC
Copy link
Contributor

@vbgl : what is your time line for the 8.10 release?

@Zimmi48
Copy link
Member

Zimmi48 commented Jul 25, 2019

@MSoegtropIMC I have now unfrozen the repository and made you a collaborator. Note that even in the event that you were late for the 8.10.0 release, it would still make sense to update the v8.10 branch of Ltac2, produce a new release, and include it in the 8.10.1 Windows installer, so I would say no rush.

@MSoegtropIMC
Copy link
Contributor

@Zimmi48 : thanks! I will do a back port for 8.10 then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: enhancement Enhancement to an existing user-facing feature, tactic, etc. part: ltac2 Issues and PRs related to the (in development) Ltac2 tactic langauge. part: standard library The standard library stdlib.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants