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

Add value type parameters RFC #174

Closed
wants to merge 1 commit into from

Conversation

Zoxc
Copy link

@Zoxc Zoxc commented Jul 21, 2014

@huonw
Copy link
Member

huonw commented Jul 21, 2014

Thanks for writing this up! This is definitely a desirable feature but should be able to be added after 1.0 in a backward compatible way.

Also it seems quite similar to #56, could read over that and highlight any differences? (If there aren't any meaningful ones, we can close this with the postponed label too.)

@emberian
Copy link
Member

This RFC is incredibly light on details, and offers no concrete analysis on the impact to the type system or how this would be implemented, only presenting a "feel" for what this feature should look + act like. Specific feedback coming later, short on time atm.

);
)

fn print_int<num>() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this still has to be const num: int

@lilyball
Copy link
Contributor

Allowing uint is useful primarily for [T, ..n]. I can see why bool might be useful, although it's less obviously necessary than uint. I'm not at all convinced that any other type is worth supporting. Notably, I think arbitrary parameterized types might be better done via a different mechanism, that of associated values for traits (not associated types, but values).

I discussed this already with @Zoxc in IRC, but the short overview is as follows:

A trait can declare associated values via the static keyword. This might look like

pub trait Numeric {
    static MAX_VALUE: Self;
    static MIN_VALUE: Self;
    ...
}

Similarly, impl blocks can then contain statics in addition to the existing fns. This would then look like

impl Numeric for uint {
    static MAX_VALUE: uint = -1u;
    static MIN_VALUE: uint = 0u;
    ...
}

With this ability, you can now provide the desired generic parameterized values by instead using a generic type that's bounded on a trait with the necessary values.

This approach should work for all generic value parameters. There's still a desire for uint parameters simply to avoid having to define a new type for every single possible numeric value that might want to be used. bool parameters could reasonably be represented using a trait Boolean and two phantom types (one for true, one for false), but that is a little cumbersome. Other value types I think have no compelling need to have special support when associated values should work just fine for them.

This would also generalize into an associated type scheme where trait and impl blocks can contain type definitions as well. Traits and impls would then effectively be mods, although I think it's reasonable to still prevent foreign fns and other mods from being defined inside them (but I could be convinced that perhaps it should be opened up to all items). This would work well for associated values because you could then use nested types as the phantom types you need.

Combined with field offsets (RFC PR #175), this would allow you to basically define a trait that requires implementors to provide certain fields. Clients of the trait can then access the fields via the associated values, and as long as the FieldOffset type properly inlines its accessors, this would theoretically let LLVM optimize the access down to the same code it would use for directly accessing the field of the instance type. The following is an example of doing this, where a single object provides information about multiple fields:

trait Offset<T,Ty> {
    static OFFSET: FieldOffset<T,Ty>;
}

struct Object {
    x: uint,
    y: uint
}

impl Object {
    /// Phantom nested type that conforms to Offset for field `x`
    struct FieldX;
    /// Phantom nested type that conforms to Offset for field `y`
    struct FieldY;

    // this impl could alternatively go one level up and use `for Object::FieldX`
    // but I think it makes a bit more sense to be nested too
    impl Offset<Object,uint> for FieldX {
        static OFFSET: FieldOffset<Object,uint> = offsetof Object.x;
    }

    impl Offset<Object,uint> for FieldY {
        static OFFSET: FieldOffset<Object,uint> = offsetof Object.y;
    }
}

With the above, you can now write code that looks like

fn foo<T, Field: Offset<T,uint>>(x: &mut T) {
    // double the given field
    *Field::OFFSET.get_mut(x) *= 2;
    ...
}

fn bar(x: &mut Object) {
    foo::<Object, Object::FieldX>(x);
}

The above is kind of trivial, but it actually represents something you can't do today, which is to provide mutable access to an object and to a field within the object simultaneously. Normally to provide field access you'd pass a &mut uint but that can't coexist with the &mut T. There are also more compelling examples with parameterizing types with Offset instead of functions (e.g. the internal-storage linked list that @Zoxc wants Field Offsets for).

A while back Servo wanted to be able to have traits define fields that implementors must provide. This was so the DOM implementation could directly access various common fields across DOM nodes without needing struct inheritance. Assuming it optimizes as well as I hope it will, I think associated values + field offsets would actually solve this need.

@lilyball lilyball mentioned this pull request Jul 22, 2014
@Zoxc
Copy link
Author

Zoxc commented Jul 22, 2014

@kballard The entire point of this is to avoid having a type per value, although grouping often used multiple values together in a single type with associated items can be an alternative in certain cases. Furthermore your example doesn't use OFFSET in an static, just in a regular expressions, so it doesn't even need associated items.

There doesn't seem to be any significant differences with #56, so you can postpone this too.

@lilyball
Copy link
Contributor

@Zoxc The use of OFFSET in a static is because it would then be usable in constexpr situations too, although I don't know how relevant that is at the moment.

@brson
Copy link
Contributor

brson commented Aug 1, 2014

This feature comes up often. It's desirable, and it will probably happen, but not before 1.0. Let's postpone this and come back to it in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
postponed RFCs that have been postponed and may be revisited at a later time.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants