Skip to content

[Add] truncate properties to Data.Vec.Properties #2795

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

e-mniang
Copy link
Contributor

@e-mniang e-mniang commented Jul 30, 2025

As suggested by @jamesmckinna, this PR adds several equational properties for truncate to Data.Vec.Properties, extending the standard vector operations toolkit with useful lemmas for reasoning about truncate.

Added properties:

- take-updateAt : applying updateAt before or after take yields the same result on valid indices

- truncate-zipWith : truncate commutes with zipWith on vectors of equal length

- truncate-zipWith-truncate : generalizes truncate-zipWith when one vector is already truncated

- zipWith-truncate : zipWith followed by truncate equals truncating the inputs first, when lengths match

- zipWith-truncate₁ : a more general version of zipWith-truncate with shifted truncation offsets

- updateAt-truncate : rewriting updateAt after truncate via take equivalence

- truncate++drop≡id : a vector can be recovered by appending its truncated prefix and dropped suffix

- truncate-map : truncate commutes with map

@e-mniang e-mniang marked this pull request as ready for review July 30, 2025 18:52
@MatthewDaggitt MatthewDaggitt added this to the v2.4 milestone Jul 31, 2025
@jamesmckinna
Copy link
Contributor

jamesmckinna commented Jul 31, 2025

@MatthewDaggitt are you happy to proceed with this PR, and its predecessor #2769 for v2.4, and only consider #2770 / #2787 downstream, or should we move to reconcile them all together?

UPDATED: I'll review once the direction of travel is (a bit) clearer...

@MatthewDaggitt
Copy link
Contributor

Yes, I'm happy to add these now and then the breaking changes in v3.0.

Copy link
Contributor

@jamesmckinna jamesmckinna left a comment

Choose a reason for hiding this comment

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

Lots of things to fix, hopefully all (most?) of which will simplify what's here.

CHANGELOG.md Outdated
Comment on lines 48 to 49
take-updateAt : (f : A → A) {m n : ℕ} (xs : Vec A (m + n)) (i : Fin m) →
updateAt (take m xs) i f ≡ take m (updateAt xs (inject≤ i (m≤m+n m n)) f)
Copy link
Contributor

Choose a reason for hiding this comment

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

Several things (which will be recapitulated below):

  • indentation/alignment so that updateAt in LHS aligns with beginning of type signature on line above
  • omit {m n : ℕ} from type signature:
    • they're inferrable, and/so should be captured by a variable declaration already
    • such arguments should, where possible, never be non-prenex (otherwise, turning them into variables would be a breaking change!)
  • I'll double down on my comment on [Add] padRight properties to Data.Vec.Properties #2769 , and say that the argument order (in particular, the position of the f binding) should mirror that for updateAt itself, if only for consistency's sake

CHANGELOG.md Outdated
Comment on lines 51 to 52
truncate-zipWith : (f : A → B → C) (m≤n : m ≤ n) (xs : Vec A n) (ys : Vec B n) →
truncate m≤n (zipWith f xs ys) ≡ zipWith f (truncate m≤n xs) (truncate m≤n ys)
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto. re: alignment

CHANGELOG.md Outdated
Comment on lines 54 to 55
truncate-zipWith-truncate : truncate o≤m (zipWith f (truncate m≤n xs) ys) ≡
zipWith f (truncate o≤n xs) (truncate o≤m ys)
Copy link
Contributor

Choose a reason for hiding this comment

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

Besides alignment, and the quantifier prefix!, here you need to inline the let binding for o≤n, as otherwise it's out of scope:

Suggested change
truncate-zipWith-truncate : truncate o≤m (zipWith f (truncate m≤n xs) ys) ≡
zipWith f (truncate o≤n xs) (truncate o≤m ys)
truncate-zipWith-truncate : truncate o≤m (zipWith f (truncate m≤n xs) ys) ≡
zipWith f (truncate (≤-trans o≤m m≤n) xs) (truncate o≤m ys)

CHANGELOG.md Outdated
Comment on lines 57 to 58
zipWith-truncate : zipWith f (truncate p≤p+q xs) (truncate p≤p+q ys) ≡
truncate p≤p+q (zipWith f xs ys)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why you've changed to p and q here, rather than m and n?
Also, we 'know' that p ≤ p + q, so the (presumably) mnemonic name must be carrying other information? If not, simply use m≤n?

CHANGELOG.md Outdated
Comment on lines 60 to 61
zipWith-truncate₁ : zipWith f (truncate o≤o+m+n xs) (truncate (o≤o+m) ys) ≡
truncate (o≤o+m) (zipWith f (truncate (o+m≤o+m+n) xs) ys)
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly here, the names are getting very unwieldy... I'll look at the code to see what might be going on...

truncate++drop≡id {m = m} {n} xs = begin
truncate (m≤m+n m n) xs ++ drop m xs ≡⟨ cong (_++ drop m xs) (take≡truncate m xs) ⟨
take m xs ++ drop m xs ≡⟨ take++drop≡id m xs ⟩
xs ∎ where open ≡-Reasoning
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

@@ -472,6 +541,20 @@ toList-map : ∀ (f : A → B) (xs : Vec A n) →
toList-map f [] = refl
toList-map f (x ∷ xs) = cong (f x List.∷_) (toList-map f xs)

truncate-map : (f : A → B) (m : ℕ) (m≤n : m ≤ n) (xs : Vec A n) →
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
truncate-map : (f : A B) (m : ℕ) (m≤n : m ≤ n) (xs : Vec A n) →
truncate-map : (f : A B) (m≤n : m ≤ n) (xs : Vec A n) →

@@ -472,6 +541,20 @@ toList-map : ∀ (f : A → B) (xs : Vec A n) →
toList-map f [] = refl
toList-map f (x ∷ xs) = cong (f x List.∷_) (toList-map f xs)

truncate-map : (f : A → B) (m : ℕ) (m≤n : m ≤ n) (xs : Vec A n) →
map f (truncate m≤n xs) ≡ truncate m≤n (map f xs)
truncate-map {n = n} f m m≤n xs =
Copy link
Contributor

Choose a reason for hiding this comment

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

... with corresponding

Suggested change
truncate-map {n = n} f m m≤n xs =
truncate-map {m = m} {n = n} f m≤n xs =

take m (cast eq (map f xs)) ≡⟨ truncate≡take m≤n (map f xs) eq ⟨
truncate m≤n (map f xs) ∎
where
.eq : n ≡ m + (n ∸ m)
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to mark eq as irrelevant; it's the proof which is ignored by cast etc.

Suggested change
.eq : n ≡ m + (n ∸ m)
eq : n ≡ m + (n ∸ m)

truncate m≤n (map f xs) ∎
where
.eq : n ≡ m + (n ∸ m)
eq = sym (proj₂ (m≤n⇒∃[o]m+o≡n m≤n))
Copy link
Contributor

Choose a reason for hiding this comment

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

In fact, Data.Nat.Properties.guarded-∸≗∸ is (more like) what you need here... and could then be inlined?

Copy link
Contributor

Choose a reason for hiding this comment

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

See also padRight-drop′ and padRight-take′ that I've added to #2787 for comparison.

@jamesmckinna
Copy link
Contributor

Thanks for the second contribution!
Once the backports #2799 from v2.3 have been merged into master, it will make sense to fix up the CHANGELOG conflicts, merge master into this branch, and then look at the suggested fixes.

@e-mniang
Copy link
Contributor Author

e-mniang commented Aug 6, 2025

Hi!
Thanks for your review. I made some changes based on what you said and deleted most of the things that could conflict with the Fairbairn threshold.
Let me know if anything else needs corrections.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants