Skip to content

Commit 9171e73

Browse files
authored
Semigroup generic over Rhs (#493)
Adds support for implementing semigroup for types that can plus_equals from another type, for example vectors and slices. Fixes a bug where the semigroup implementation for `Vec` would add overhanging elements twice, i.e., `[1,2] + [1,1,1]` would result in `[2,3,2`] instead of `[2,3,1]`. This leaves one quirk where `is_zero` does not depend on `Rhs`, so Rust cannot figure out which implementation to use when two are in scope, forcing the caller to phrase it as `<R as Semigroup>::is_zero(&value)`. This only applies if `R` implements `Semigroup + Semigroup<Other>`. Signed-off-by: Moritz Hoffmann <[email protected]>
1 parent b0c8f2a commit 9171e73

File tree

1 file changed

+29
-8
lines changed

1 file changed

+29
-8
lines changed

src/difference.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ pub use self::Abelian as Diff;
2222
/// There is a light presumption of commutativity here, in that while we will largely perform addition
2323
/// in order of timestamps, for many types of timestamps there is no total order and consequently no
2424
/// obvious order to respect. Non-commutative semigroups should be used with care.
25-
pub trait Semigroup : ::std::marker::Sized + Data + Clone {
25+
pub trait Semigroup<Rhs: ?Sized = Self> : Data + Clone {
2626
/// The method of `std::ops::AddAssign`, for types that do not implement `AddAssign`.
27-
fn plus_equals(&mut self, rhs: &Self);
27+
fn plus_equals(&mut self, rhs: &Rhs);
2828
/// Returns true if the element is the additive identity.
2929
///
3030
/// This is primarily used by differential dataflow to know when it is safe to delete an update.
@@ -233,22 +233,43 @@ mod vector {
233233

234234
impl<R: Semigroup> Semigroup for Vec<R> {
235235
fn plus_equals(&mut self, rhs: &Self) {
236-
// Ensure sufficient length to receive addition.
236+
self.plus_equals(&rhs[..])
237+
}
238+
fn is_zero(&self) -> bool {
239+
self.iter().all(|x| x.is_zero())
240+
}
241+
}
242+
243+
impl<R: Semigroup> Semigroup<[R]> for Vec<R> {
244+
fn plus_equals(&mut self, rhs: &[R]) {
245+
// Apply all updates to existing elements
246+
for (index, update) in rhs.iter().enumerate().take(self.len()) {
247+
self[index].plus_equals(update);
248+
}
249+
250+
// Clone leftover elements from `rhs`
237251
while self.len() < rhs.len() {
238252
let element = &rhs[self.len()];
239253
self.push(element.clone());
240254
}
241-
242-
// As other is not longer, apply updates without tests.
243-
for (index, update) in rhs.iter().enumerate() {
244-
self[index].plus_equals(update);
245-
}
246255
}
247256
fn is_zero(&self) -> bool {
248257
self.iter().all(|x| x.is_zero())
249258
}
250259
}
251260

261+
#[cfg(test)]
262+
mod tests {
263+
use crate::difference::Semigroup;
264+
265+
#[test]
266+
fn test_semigroup_vec() {
267+
let mut a = vec![1,2,3];
268+
a.plus_equals([1,1,1,1].as_slice());
269+
assert_eq!(vec![2,3,4,1], a);
270+
}
271+
}
272+
252273
impl<R: Monoid> Monoid for Vec<R> {
253274
fn zero() -> Self {
254275
Self::new()

0 commit comments

Comments
 (0)