Collective bikeshed of `#[marker] trait Foo { .. }`

Rust itself also provides precedent for may in the form of #[must_use] in the sense that both may and must are modal verbs.

Do we think that impls_ is necessary or could we leave that inferred and shorten the name to #[may_overlap] perhaps? Here, the name in #[may_overlap] is 11 characters long and shorter than in #[overlappable] which is 12 characters long.

I have a very slight preference for including impl(s) because

  • this is a pretty small feature in the grand scheme of things and not likely to be written dozens of times in a row, so a long descriptive name wins by default

  • I feel like a user whoā€™s brand new to all this trait system machinery would have a much easier time guessing what ā€œimpls may overlapā€ means on a trait than what ā€œoverlappableā€ means on a trait (thereā€™s probably some plausible but wrong meaning you could invent for ā€œoverlapping traitsā€; just look at how many people thought ā€œinherent trait implsā€ were a thing because of bare trait syntax)

1 Like

I am thinking that that is exactly what this attribute represents. "I as the API author recognize the trivialness of this Trait and am making a commitment to keep it Trivial so you as a client are free to create 3rd party implementations of this trait".

1 Like

I like this one a lot. If we're not comfortable with "trivial" or "simple", I like this one the best of anything suggested so far. Could possibly be shorted to just "may_overlap" as well.

2 Likes

I too like #[impls_may_overlap] best so far

The shorter #[may_overlap] sounds a little like it might be referring to memory overlapping somehow, which is of course nonsense for a trait, let alone an empty trait. This potential confusion is (small) cognitive load.

Longer still, but perhaps:

  • #[other_crates_may_impl]
  • #[any_crate_may_impl]

Or shorter by a couple of chars:

  • #[anyone_may_impl]
1 Like

This sounds REALLY good. I'd shorten even a bit more to:

  • #[any_may_impl]
1 Like

I'm not a huge fan of trivial because the meaning of an unsafe trait can be quite complex and nuanced, even if it has no overridable behaviour.

:+1: I think this is particularly true since it goes on the trait, of which there's only one, not on the impls, of which there are many.

2 Likes

third_party_impls?

1 Like

#[mere_proposition]

(Mostly joking.)

I think marker is a fine name, since we already call these marker traits.

I think if a trait has items that canā€™t be overridden, that trait is still a marker trait, because you canā€™t define any items in impls. I am also dubious that we would ever add this feature, since it adds complexity & irregularity and is semantically equivalent to just writing free functions.

Iā€™m more interested in the possibility that there are other things we could do with marker traits (nothing in particular comes to mind), and I think limiting the name of this attribute to the particular behavioral change weā€™re introducing today would be shortsighted.

1 Like

Yes, but, what about traits that aren't "Marker" traits (i.e. have some default, non-overridable methods) that this should work with as well? I think that was the OP's point that "Marker" is misleading because it will (potentially) need to be used with non-marker traits; hence, the commentary around "simple/trivial" traits and a hierarchy of "Trait Complexity Kinds".

I specifically addressed this in my comment:

Derp! i need to get more sleep! :slight_smile:

1 Like

(You are are aware of this, but to paint a fuller picture...)

There are two main differences which does make it semantically nonequivalent:

  1. a free function can't be called with method syntax; but a trait function can be;

    This can be dealt with by letting free functions reference self.

  2. there's no ad-hoc overloading, which is one of the main points of traits (per Wadler's "How to make ad-hoc polymorphism less ad-hoc").

1 Like

This can be worked around by having an item-less marker trait + a pre-implemented extension trait (until that pattern gets more sugar such as you suggest).

Doesnā€™t overloading make multiple implementation of a marker trait incoherent again?

2 Likes

Sure, that's what folks generally do today, but some consider it a hack.

As far as I know, No it doesn't; because all items are restricted to have the default provided definition; of course you can say that this is not overloading at all in that case...

We're diverging from the topic, but of course that's not overloading: ad hoc overloading means the ability to redefine the item according the particular type.

My point is that I think every item of a trait should be ad hoc overloadable for consistency and simplicity and comprehensibility of our system, at the expense of letting you wrote non-overloadable functions with method or associated function syntax. I'm similarly against free functions with self arguments.

That is, I think it is not worth allowing full syntactic freedom to create exactly the API syntax you might want at the expense of providing language features such that the syntax loses its connection to particular semantics. In this case, I think it is beneficial to users to be able to work from an understanding associated items are type dispatched in an ad hoc overloadable way. (My concerns about macro methods are along the same line.)

returning to the topic...

While it is true that "we already call these marker traits", I pointed out here (and @Ixrec extended it to #[trivial] et al. here) that having the name #[marker] would result in some marker traits having the annotation #[marker] while some marker traits would not. It is then unclear what a marker trait is... is it a trait with zero items (the current common definition), or is it a trait which has a #[marker] annotation on it?

The main benefit of #[marker] is as you say if we would extend the meaning of the attribute to do more things; but you also said nothing comes to mind (whereas things come to mind wrt. extending the attribute to other sorts of traits even if you are currently opposed to such extensions...).

I think that @Ixrec also hit the nail on the head when they said:

and

It seems to me that a naming which somehow includes "overlap" takes less out of the complexity budget than other names.

3 Likes

Well, there's a good T-libs question: would we be willing (eventually, probably not immediately) to put #[marker] on everything in std::marker?

I have use-cases for explicitly not wanting Unpin to be #[marker], or at least to make it so that a manual non-overrideable opt-out is possible. This is so that macros could generate types that are guaranteed to be !Unpin for safety reasons and cannot have Unpin implā€™d by the author of the crate in which the macro is called.

1 Like