zkcrypto / pairing

Pairing-friendly elliptic curve library.
Other
338 stars 118 forks source link

Operator Overloading #85

Open afck opened 6 years ago

afck commented 6 years ago

Would it make sense to overload the arithmetic operators (std::ops) for everything that has an add, add_assign, mul or mul_assign method? I would love being able to write x += y; instead of x.add_assign(y), but I know that there are arguments against it, too. It certainly would make some heavy computation "feel" more lightweight than it is.

What's your opinion on this? If you are fine with it, I'd be happy to try and make e.g. Field: AddAssign + MulAssign + SubAssign + Neg, and CurveAffine: Mul + Neg.

ebfull commented 6 years ago

I would have loved to do that at the time. I don't think Rust had AddAssign when I first wrote this implementation as part of another library, and the alternative overloading was impossible to express as a supertrait.

I would definitely love to see the impact of that and would be happy to merge it, though I would rather not import MulAssign at callsites and rather just have all code that invokes add_assign,sub_assign,mul_assign throughout the library switch to use the operator overloading instead.

burdges commented 6 years ago

See the Eye of Sauron saga: https://github.com/dalek-cryptography/curve25519-dalek/issues/57 https://twitter.com/isislovecruft/status/839333846415998976 https://github.com/rust-lang/rfcs/pull/2147 https://github.com/dalek-cryptography/curve25519-dalek/pull/101

afck commented 6 years ago

Great, I'll give it a try, probably some time next week.

Thank you for the warnings, @burdges! :smile:

My plan for now is just to only add the operators whose implementations are already there: If there's an add_assign(&mut self, Self), use it in AddAssign<RHS = Self> etc. I wouldn't try to add lots of variants for different combinations of references right away, or hide multiple operations (e.g. clone and add_assign) behind an innocent-looking symbol ("+"). Those kinds of ergonomics improvements should probably be added separately and discussed case-by-case.

afck commented 6 years ago

I made a start with AddAssign: https://github.com/afck/pairing/commit/15169b6a4a84aae9b7c2f616eddd16d00a5a5e35 It makes Field require for<'r> ops::AddAssign<&'r Self> and removes the trait's own (conflicting) add_assign method.

Could you please have a look whether you'd be okay with that kind of implementation? Then I'll do the same for MulAssign, SubAssign, ShrAssign and ShlAssign, where appropriate. (I'm not sure whether it will be feasible for e.g. Mul.)

ebfull commented 6 years ago

One good test is to see how it impacts downstream libraries and benchmarks. I'll do this when I get a chance.

ebfull commented 6 years ago

I am very busy but I submitted a partial review.

afck commented 6 years ago

One good test is to see how it impacts downstream libraries and benchmarks.

Unfortunately it will be backwards-incompatible, since the existing method add_assign conflicts with the trait's method. (A quick fix for dependent crates is to just import the AddAssign trait; no need to replace all calls with operators.)

I'm hoping that performance shouldn't change: The implementations are identical and I carried over all the #[inline] attributes.

I am very busy but I submitted a partial review.

I can't see any comments (and didn't open a PR for this).

ebfull commented 6 years ago

I can't see any comments (and didn't open a PR for this).

Yeah, I don't know where my review was or what I was even referring to! :octocat: