AssemblyScript / assemblyscript

A TypeScript-like language for WebAssembly.
https://www.assemblyscript.org
Apache License 2.0
16.93k stars 663 forks source link

[RFC] Automatically insert relational overloading based on existing #2494

Open MaxGraey opened 2 years ago

MaxGraey commented 2 years ago

For example, if we have custom eq we can always defina ne(a, b) as !eq(a, b) implicitly but only if operator marked as "private":

class Vec2 {
   constructor(public x: f64 = 0, public y: f64 = 0) {}

   @operator("==")
   private eq(other: f64): bool {
     return this.x == other.x && this.y == other.y;
   }
   // automatically define "ne" by compiler:
   // @operator("!=")
   // private ne(other: f64): bool {
   //  return !this.eq(other);
   // }
}

similar to > / <= and < / >=:

class Vec2 {
   constructor(public x: f64 = 0, public y: f64 = 0) {}

   @operator(">")
   private gt(other: f64): bool {
     return this.x > other.x && this.y > other.y;
   }
   @operator("<")
   private lt(other: f64): bool {
     return this.x < other.x && this.y < other.y;
   }

   // automatically define "le" by compiler:
   // @operator("<=")
   // private le(other: f64): bool {
   //  return !this.gt(other);
   // }

   // automatically define "le" by compiler:
   // @operator(">=")
   // private ge(other: f64): bool {
   //  return !this.lt(other);
   // }
}

In this case we can define minimal set to ==, > and < to define full comparison domain. The rest can be overload optionally if behavior for !=, <=, >= should be different

dcodeIO commented 2 years ago

One initial thought perhaps: Operator overloads are relatively unrestricted, i.e. an eq is not required to return a bool, respectively a == is not guaranteed to be an equals check at all. Would that lead to problems?

MaxGraey commented 2 years ago

Yeah. Result type should be a bool. Good point!

But now I'm wondering should we restrict overloadings ==, != and etc to always be a bool as a result?

HerrCai0907 commented 1 year ago

Maybe a new operator overload is helpful <=> similar as cpp https://en.cppreference.com/w/cpp/language/operator_comparison#Three-way_comparison

I think for most of case it's enough. eq / le / ge fulfills mathematical requirement. The code should be like:

class Vec {
  constructor(public x: f64 = 0, public y: f64 = 0) {}

  norm2(): f64 {
    return this.x * this.x + this.y * this.y;
  }
  //  @operator("<=>")
  private threeWayCmp(other: Vec): i32 {
    const thisNorm2 = this.norm2();
    const otherNorm2 = other.norm2();
    return thisNorm2 - otherNorm2;
  }

  // automatically define by compiler:
  @operator("==")
  private eq(other: Vec): bool {
    return this.threeWayCmp(other) == 0;
  }
  @operator("!=")
  private ne(other: Vec): bool {
    return this.threeWayCmp(other) != 0;
  }
  @operator(">")
  private ge(other: Vec): bool {
    return this.threeWayCmp(other) > 0;
  }
  @operator("<=")
  private nge(other: Vec): bool {
    return this.threeWayCmp(other) <= 0;
  }
  @operator(">")
  private le(other: Vec): bool {
    return this.threeWayCmp(other) < 0;
  }
  @operator("<=")
  private nle(other: Vec): bool {
    return this.threeWayCmp(other) >= 0;
  }
}
MaxGraey commented 1 year ago

Maybe a new operator overload is helpful <=>

It is not as universal as it may seem. For example, it won't work for unsigned int and floating points. Or rather, you will have to do rather non-trivial things to make it work. You can check it out here

HerrCai0907 commented 1 year ago

What I mean is just adding a decorator @operator("<=>") instead of adding a total new operator. The aim is that by this decorator, we don't need to write lots of similar compare code. It just a syntactic sugar. Of course, your suggestion is more flexible but I think self-defined >= <= == without mathematical meaning is very corner case.

it won't work for unsigned int and floating points I do not get the point why this design won't work for them.