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

feat: lemmas characterising zipWith(All) via get? #325

Merged
merged 1 commit into from
Nov 1, 2023
Merged

Conversation

semorrison
Copy link
Collaborator

No description provided.

@semorrison semorrison added the awaiting-review This PR is ready for review; the author thinks it is ready to be merged. label Oct 27, 2023
@@ -199,6 +199,32 @@ theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} (i : δ):
(zipWith f l₁ l₂).foldl g i = (zip l₁ l₂).foldl (fun r p => g r (f p.1 p.2)) i := by
induction l₁ generalizing i l₂ <;> cases l₂ <;> simp_all

theorem zipWith_get? {f : α → β → γ} :
(List.zipWith f as bs).get? i = match as.get? i, bs.get? i with
| some a, some b => some (f a b) | _, _ => none := by
Copy link
Contributor

Choose a reason for hiding this comment

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

Will the match need to generate variable names if this is a simp rule? We could avoid it with something like the following:

theorem zipWith_get? {f : α → β → γ} :
    (List.zipWith f as bs).get? i =
        if p : i < as.length ∧ i < bs.length then
          some (f (as.get ⟨i, p.left⟩) (bs.get ⟨i, p.right⟩))
        else
          none := by
  induction as generalizing bs i with
  | nil => simp [Nat.not_lt_zero]
  | cons a as aih =>
    cases bs with
      | nil => simp [Nat.not_lt_zero]
      | cons b bs =>
        cases i <;> simp_all [Nat.zero_lt_succ, Nat.succ_lt_succ_iff]

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@joehendrix, sorry, I don't understand what the concern is with variable names. In my application I can simp by this lemma just fine, and get a goal something like:

⊢ Option.getD
    (match List.get? xs i, List.get? ys i with
    | some a, some b => some (a * b)
    | x, x_1 => none)
    0 =
  Option.getD (List.get? xs i) 0 * Option.getD (List.get? ys i) 0

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is it the x and x_1 names that you're concerned about?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

(You might be able to see from my example why I wanted a match on xs.get? i, ys.get? i: in my application the next step is cases xs.get? i <;> cases ys.get? i, which operates on both the LHS (i.e. the result of this lemma), and the RHS.)

Copy link
Contributor

Choose a reason for hiding this comment

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

@semorrison I was thinking thinking of all the names that potentially need to be synthesized in the match (a, b, and the two underscores). Those could be brittle if user's need to refer to them since it depends on what other variables are in scope.

One could also write the lemmas as the code below:

theorem zipWith_get? {f : α → β → γ} : (List.zipWith f as bs).get? i = f <$> as.get? i <*> bs.get? i := by ...

I think this is more elegant than my initial suggestion, but I don't think there is a similar elegant lemma for zipWithAll.

I was also thinking if we could integrate simp and linear arithmetic, then it may be advantageous to go directly to right-hand sides with arithmetic conditions. We could postpone that sort of decision until linear arithmetic is integrated.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm skeptical that this naming collision problem is any worse than having a lambda on the right hand side.

e.g. the line above in this file is:

theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} (i : δ):
    (zipWith f l₁ l₂).foldl g i = (zip l₁ l₂).foldl (fun r p => g r (f p.1 p.2)) i := by

Are the r and p problematic? If not, why are the names in the match statement different?

Copy link
Member

@digama0 digama0 Nov 1, 2023

Choose a reason for hiding this comment

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

Agreed with Scott, generally speaking it shouldn't matter what variable names are used inside lambdas and match expressions, the names only become relevant once they enter the context via intro x or similar and all such mechanisms provide a way to give a name (and if not you can always use rename_i or next to name them).

In any case, it's not a simp lemma so it doesn't really matter for the purposes of this PR, this is just one option available to the user, if it results in a bad or inconvenient goal they can use something else.

@digama0 digama0 merged commit d2007e4 into main Nov 1, 2023
2 checks passed
@digama0 digama0 deleted the zipWith_get branch November 1, 2023 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting-review This PR is ready for review; the author thinks it is ready to be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants