Skip to content
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

What about traits? #5

Closed
nical opened this issue May 30, 2017 · 10 comments
Closed

What about traits? #5

nical opened this issue May 30, 2017 · 10 comments
Labels

Comments

@nical
Copy link

nical commented May 30, 2017

There's been discussions about providing standard data structures for common math types, but the interop question can also be addressed through traits. What would be the pros and cons of mint defining traits instead of data structures?

the kind of traits I am thinking about could be like:

pub trait Vector<T> {
    pub fn dot(&self, other: &Self) -> T;
    // ... whatever else is common enough and deserves to be here
}

pub trait Vector3<T> : Vector<T> {
    fn x(&self) -> T;
    fn y(&self) -> T;
    fn z(&self) -> T;
    // ... whatever else is common enough and deserves to be here
}

pub trait Point3<T> {
    fn x(&self) -> T;
    fn y(&self) -> T;
    fn z(&self) -> T;
    // ... whatever else is common enough and deserves to be here
}

pub trait Transform<From, To> {
    fn transform(&self, a: &From) -> To;
    // etc..
}

// more traits, etc.

Or could look very different, I don't know. I just want to get the discussion started. I have a feeling that I would prefer standard traits over standard data structures, probably because adding new data structures next to the ones I am already happily using feels redundant and cumbersome, but I'd need to explore this more to have an idea of how useful that would be.

@kvark
Copy link
Owner

kvark commented May 30, 2017

Thank you for sparking this discussion, @nical !

I feel that traits are on the entirely opposite side of the problem. Traits describe behavior, where the original idea behind mint was to standardize the content.

Suppose you use a library that exposes something satisfying mint::Vector trait. Now you want to process it with nalgebra. What do you do?

The solution I'd prefer is to have mint describing types only (plus a few From/Into conversions). Then we may have another crate (mint_traits?) with standard traits like Transform3 that are implemented for the standard types as well as (potentially) native types of other libraries.

@nical
Copy link
Author

nical commented May 30, 2017

The idea would be that euclid or naglebra would implement ways to make conversion to mint traits easy (like From).
An interface that wants to be "more standard" could be generic over the mint trait or some trait that describes an easy conversion between the mint trait and nalgebra's structure.

@nical
Copy link
Author

nical commented May 30, 2017

@nical
Copy link
Author

nical commented May 30, 2017

For example if you look at the lyon_bezier crate, all of the members are publicly exposed so it can't expose an API with standard types unless all members are themselves of that standard type and then I go from having a library that is very nice to use with euclid to one that is a bit awkward to use with every library (not very appealing from my point of view as a user and maintainer of the crate) and more annoying to maintain.

However, the bezier curve segments could maybe be generic over a Point2<F32> trait that provides the interface I need to do the bezier math. It would make the crate a bit more tedious to write (generics all over the place), but it would seamlessly integrate with the other lyon crates that use euclid or any other math crate that implements the mint traits.

@kvark
Copy link
Owner

kvark commented Jun 1, 2017

If you expose mint types in that (wonderful!) bezier crate and have a separate library with standard traits that happen to be implemented for mint types, then it's internals are going to be pretty and easy to maintain.

@nical
Copy link
Author

nical commented Jun 1, 2017

Kinda, but having mint declare data structures and logic (the traits and implementation of the traits), is equivalent to creating a new full featured math library. At this point mint isn't lighter wait than euclid or the others.

Plus, for lyon_bezier to be nice to use with the rest of the lyon crates which heavily use euclid, it needs to store euclid objects (be it explicitly or generically by storing a trait that euclid points implement). If a bezier segment stores its points generically using traits (not types), I can seemlessly integrate with the rest of my code, whereas with mint types I have to jungle between two ways to represent the same thing.

@kvark
Copy link
Owner

kvark commented Jun 1, 2017

is equivalent to creating a new full featured math library

I was just about to put it into README ;)
But yes, that is true. However, the point can be somewhat softened by:

  1. traits don't have to be in the same crate, could be mint_traits. Edit: technically, traits and their implementations would deserve separate (dependent) features.
  2. traits could be behind a non-default feature gate (my preference). Similarly, operators can be gated too.

or lyon_bezier to be nice to use with the rest of the lyon crates which heavily use euclid

It would be as nice to use if the rest of lyon crates was using mint ;)

@Ralith
Copy link
Contributor

Ralith commented Jun 2, 2017

One trouble with traits is that the set of potentially useful operations is much larger, and has a much larger set of design choices to be made, than the set of potentially useful interface types. Mint's current direction allows it to be lightweight and minimal, and imposes no significant design constraints on other libraries. lyon_bezier, for example, would play adequately well with mint by merit of suitable From/Into implementations existing for euclid types, rather than having to be invasively refactored into generic, less-predictable, slower-to-compile code.

@nical
Copy link
Author

nical commented Jun 2, 2017

The traits don't need to expose the entire set of potentially useful operations. In fact it could be as small as the set of things you can do out of a mint type, so x(), y(), z() and new(x, y, z) are enough to provide the same service as mint types if you want to keep it small.

If as a user of the trait you want to add missing operations, you can simply do it, even in a separate crate. For example, injecting a square_length method to implementors of a trait Vector2<T> that only exposes x and y:

trait SquareLength<T> {
    fn square_length(&self) -> T;
}

impl<V, T> SquareLength<T> for V
where
  V: Vector2<T>,
  T: Add<Output=T> + Mul<Output=T>
{
    fn square_length(&self) -> T {
        self.x() * self.x() + self.y() + self.y()
    }
}

With mint types, lyon_bezier would have to either copy out every member into an euclid type before doing anything and then back after operations are performed (the methods are on average 5-6 lines long so it is already quite invasive), or have from/into called every time a member is used which is not worth the pain.
I agree with the problem that generics worsen compile times, but I don't see what's less predictable about accessing x through a trait method rather than indexing an array.

Another advantage of traits:
Euclid has a great feature that all vectors and transforms are tagged with units (or spaces depending on how you choose to think of it) in a very ergonomic way. This lets us avoid adding a vector in screen space to a vector in world space, CSS stacking-context space (or one of the dozen other coordinate systems we have to jungle with). This has allowed us to find a lot of bugs (enough bugs to want to maintain a dedicated vector library), and has prevented tons more. mint types basically lose all of the benefits of this feature, because you need to convert into a unit-less vector type at the API boundary where you are exposing your standard types, so you are dropping all of this important type information.
traits can easily let us support this because vectors with different units are different types and so different implementations of the same traits. The trait system naturally won't let you assign a vector in a space to a vector in another space, even if they are expressed generically.

@nical
Copy link
Author

nical commented Jun 2, 2017

It would be as nice to use if the rest of lyon crates was using mint ;)

you don't need to create a new crate if the goal is that everyone switches to it and only uses it (realistically people won't). If that's what we are after, we'd be much better off saying that cgmath is the standard (I'd say euclid but I guess cgmath already has more users), and trying to convince other math libraries to make it easy to interoperate with cgmath.

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

No branches or pull requests

3 participants