Say I am dealing with 2D vectors, 3D vectors and matrices (and probably more data types) in my app, and I have to do math operations on them.
If not restricted to plain objects, I could define classes and organize those operations as methods:
class Vec2 {
...
add(other: Vec2) -> Vec2 { ... }
withZ(z: number) -> Vec3 {
return new Vec3(this.x, this.y, z);
}
// returns (a, b, c), where ax + by + c = 0 is the line going through this and other
line(other: Vec2) -> Vec3 {
return this.withZ(1).cross(other.withZ(1));
}
...
}
...
class Matrix {
...
add(other: Matrix) -> Matrix { ... }
mul(other: Matrix) -> Matrix { ... }
...
}
However, with Redux I should store them as serializable objects, so:
Vec2
is[number, number]
Vec3
is[number, number, number]
Matrix
isnumber[][]
With all the methods separated from the data, I have to rewrite them as conventional functions working on those plain data, but how should I organize all those method-like functions?
I have come up with 3 plans by far:
-
Put all functions at the same level, with good old C-style naming (or some ad-hoc naming):
type Vec2 = [number, number]; // similar for Vec3 & Matrix export function vec2Add ... export function vec2WithZ ... export function vec2Line(v1: Vec2, v2: Vec2) -> Vec3 { // so nostalgic writing code like this! return vec3Cross(vec2WithZ(v1, 1), vec2WithZ(v2, 1)); } ... export function matrixAdd ... export function matrixMul ...
Another downside shows up when importing those functions, I have to explicitly list every function used (rather than a single
#include
in C). -
Wrap plain data in rich objects, do the computation, and unwrap
type Vec2 = [number, number]; // plain data class Vec2 { ... } // rich object // FIXME name clash // reducer be like: const somePoint = new Vec2(state.somePoint); const newLines = action.newPoints.map((p) => somePoint.line(new Vec2(p))); if (...) { return { ...state, lines: newLines.map((l) => l.plainData()), ... }; } ...
This looks pretty nice (and quite like what math.js does), except for the naming: I have to name both the plain type and the class. Unfortunately I am not sure which one I should name as simplly
Vec2
(and make the other type name more verbose). -
Group functions in namespaces (global objects, classes or even modules):
// using object export const Vec2Op = { add: ... withZ: ... line: ... }; // using class export class MatrixOp { static add ... static mul ... }
This also makes the code quite verbose.
Is there a better way of organizing those functions?