per-coding-style icon indicating copy to clipboard operation
per-coding-style copied to clipboard

Add Attributes style guide

Open Crell opened this issue 3 years ago • 26 comments

This is based on what I've seen in the wild, with a healthy dose of my own judgement. It seems consistent with the rest of the document.

Resolves #8

Crell avatar Jul 08 '22 21:07 Crell

I'm not really a fan of including multiple attribute in a single #[...] especially when the attribute block isn't inline'd with a property.

For example, it feels unnecessarily hard to spot the Api at the end though syntax highlighting does help:

#[SomeLongAttributeWithABunchOfProperties(some: 'thing', i: 'will', definitely: 'see'), Api]
public function foo()
{
   ...
}

KorvinSzanto avatar Jul 17 '22 18:07 KorvinSzanto

I don't like it either, but the language allows for it. A coding standard of "never use this language feature" seems... silly.

What I could see is maybe saying that attributes with arguments must always be in their own attribute block, but that's a fairly arbitrary rule I've not seen specified anywhere else. (I personally always put attributes in separate blocks, but I don't know if that's typical.)

Crell avatar Jul 18 '22 16:07 Crell

We definitely restrict language features with the goal of improving readability, a good example of that is compound namespaces under 3:

Compound namespaces with a depth of more than two MUST NOT be used

A good thing to keep in mind is that folks are welcome to say "We adhere to the Coding Style PER with the following changes:" and alter any hard requirements to fit their needs and so we can be a little more restrictive if readability benefits enough.

KorvinSzanto avatar Jul 18 '22 16:07 KorvinSzanto

Sure, but we don't forbid compound namespaces entirely. That would be the equivalent here. I'm not comfortable just blanket disallowing a language feature, even if I never use it personally. (I never, ever, use compound namespaces, either.)

Would you be OK with a "only use if they're all argument-free" or something like that?

Crell avatar Jul 18 '22 19:07 Crell

There's a SHOULD keyword for that purpose.

samdark avatar Jul 18 '22 19:07 samdark

Would you be OK with a "only use if they're all argument-free" or something like that?

That does help but I'm not convinced that Api is easily seen here even without any arguments:

#[SomeLongAttributeWithABunchOfProperties, Api]

I'd like to hear what other people think while we wait for the 2 week minimum on this PR.

KorvinSzanto avatar Jul 18 '22 21:07 KorvinSzanto

I agree that soft-limiting it is a good idea.

samdark avatar Jul 19 '22 07:07 samdark

I tried tweaking the wording a little.

Note that this still says MAY, only. So someone always and forever putting every attribute in its own block would be fully compliant.

Crell avatar Jul 19 '22 17:07 Crell

Also rebased, so the section numbers all moved around.

Crell avatar Jul 19 '22 17:07 Crell

Hello. Could you add in the example the code block like this:

    public function __construct(
        #[Load(context: 'foo', bar: true)]
        private readonly FooService $fooService,

        #[LoadProxy(context: 'bar')]
        private readonly BarService $barService,
    ) {
    }

It can be useful to demonstrate the attributes usage in conjunction with the property promotion feature and the next rule: Blank lines MAY be added to improve readability and to indicate related blocks of code except where explicitly forbidden.

roxblnfk avatar Jul 21 '22 13:07 roxblnfk

Yeah, we should include a constructor, good call. For the blank lines, that's not in there now and I've heard differing opinions in the wild on it. I'm in favor of allowing them, but what does the rest of the WG think?

Crell avatar Jul 21 '22 13:07 Crell

Allowing blank lines is fine.

samdark avatar Jul 21 '22 17:07 samdark

For the blank lines, that's not in there now and I've heard differing opinions in the wild on it. I'm in favor of allowing them, but what does the rest of the WG think?

Just leave it unstated and the "blank lines MAY be added to improve readability" rule will apply. Blank lines can be added most places to improve readability and I don't think this should be any different.

KorvinSzanto avatar Jul 26 '22 18:07 KorvinSzanto

I agree with @KorvinSzanto said here:

I'm not really a fan of including multiple attribute in a single #[...] especially when the attribute block isn't inline'd with a property.

I'm not a fan of it too, but if we want to allow multiple attributes in a single block, we can recommend to split in multiple lines like:

#[
  SomeLongAttributeWithABunchOfProperties(some: 'thing', i: 'will', definitely: 'see'),
  AnotherVeryLongAttributeHere(with: 'some', params: true),
  Api
]
public function foo() { ... }

alekitto avatar Jul 27 '22 08:07 alekitto

But at that point wouldn't it be easier and just as effective to use separate blocks?

I don't know how to say that really long ones are a bad idea, but #[Foo, Bar] is fine. And I'm really not on board with saying that isn't OK.

Crell avatar Jul 27 '22 22:07 Crell

But at that point wouldn't it be easier and just as effective to use separate blocks?

That's why I prefer separate blocks

I don't know how to say that really long ones are a bad idea, but #[Foo, Bar] is fine.

The question is: where's the limit?
Foo and Bar are not really common in real-world applications and for clarity's sake I'd prefer to use separate blocks even in this case, rather than risk to miss an attribute just because the screen is not large enough to show it.

alekitto avatar Aug 01 '22 21:08 alekitto

The question is: where's the limit?

Limit is 120 chars per line :)

I don't think there is a need to set any limits on the number of attributes, their parameters, etc. Allow the user to design attributes in different ways, and if the user wants to design the code beautifully, he will do it.

roxblnfk avatar Aug 02 '22 06:08 roxblnfk

Limit is 120 chars per line :)

Fair enough.

The thing I want to note is that

#[SomeLongAttributeWithABunchOfProperties(some: 'thing', i: 'will', definitely: 'see'), Api]

is 92 chars long (100 chars with 8 spaces of indentation), so it is ok with the limit, but still the Api part is completely transparent at first sight.

That said, if we want to allow multiple attributes as longs as they don't break the chars per line rule, it's ok for me.

alekitto avatar Aug 02 '22 09:08 alekitto

Limit is 120 chars per line :)

That's a soft limit and not a requirement. If we wanted that cemented for attributes we'd have to explicitly state so. I don't really think we want to limit the line length of attributes more than what we already have since attributes are likely to see deep nesting and to use constants with long names.

Allow the user to design attributes in different ways, and if the user wants to design the code beautifully, he will do it.

This is the antithesis of a code style guideline, the point of having a guideline is to ensure code looks the same across authors and across projects. There are definitely places where we can take this approach - and this might be one of them - but we should err on the side of defining things more and leaving things undefined less.


Responding to @Crell:

I don't know how to say that really long ones are a bad idea, but #[Foo, Bar] is fine. And I'm really not on board with saying that isn't OK.

I'm not convinced that allowing #[Foo, Bar] adds value. To me it makes more sense to disallow it and have folks who'd like to use compound attributes specifically allow it like we do with folks who would prefer tabs over spaces.

If we could just think of a way to articulate the thinking of "Long complex = bad, short simple = good" that doesn't leave things entirely subjective like "reasonably short" does it'd be easier to include. That said I think the rest of this looks good to me and I'd be willing to merge it if we did the following:

  1. Remove the bit that allows compound attributes like #[Foo, Bar] as well as the examples that show compound attributes
  2. Leave compound attributes unspecified
  3. Open an issue to continue discussion so we can decide how to proceed

KorvinSzanto avatar Aug 02 '22 20:08 KorvinSzanto

We can keep discussing in another thread, but I want to reiterate that "the language explicitly supports syntax X, but we're forbidding you from using it" is not a position I can support, even if I personally have never used compound attributes. So it's all a matter of finding a better/more precise way of saying "reasonably short" (which I agree is suboptimal). I also don't think "well people can just violate the spec if they really want to" is a good position to take. That undermines the entire idea of having a unified style guide. (Yes, we cannot enforce it, but we want it to be something that the majority of the ecosystem can use exactly as-is or it's ineffective.)

Crell avatar Aug 03 '22 15:08 Crell

To be clear what I'm asking for here is to leave this unspecified for now, not to disallow it.

KorvinSzanto avatar Aug 08 '22 16:08 KorvinSzanto

So something like this?

Crell avatar Aug 08 '22 20:08 Crell

No not quite, your revision leaves parts discussing compound attributes and includes examples of them. What I'm asking for is to remove the parts that refer to compound attributes and leave compound attributes entirely unspecified for now. I can go through and make a suggested change at some point this week while I'm at a conference if you're still unsure of what that'd look like.

If we're not able to extract that contentious bit from this PR for now this PR will have to hang out until all parts are agreeable which is totally fine.

KorvinSzanto avatar Aug 09 '22 15:08 KorvinSzanto

We cannot reasonably pretend that they don't exist. I'd rather acknowledge their existence and put reasonable bounds on them (as here) and punt on the "when you should use them" question than punt on their existence entirely.

Crell avatar Aug 10 '22 14:08 Crell

Can we get this resolved? I don't really see much alternative to what is listed here now. Ignoring the existence of compound attributes is not viable, and forbidding the use of a valid language feature is also not viable. That leaves us with some variation on "use sparingly" as the only option.

Crell avatar Sep 20 '22 23:09 Crell

Ignoring the existence of compound attributes is not viable, and forbidding the use of a valid language feature is also not viable

What makes this a feature that can't be ignored? For example, having a comma after the last item of an array is a valid construct even if it's on a single line. What makes those different? It's not like there's no alternative to compound attributes, right?

mathroc avatar Sep 21 '22 07:09 mathroc

I haven't heard any other WG members concerned about this so I'll merge as is so we can move forward. If anyone else wants to discuss this please open an issue.

KorvinSzanto avatar Nov 10 '22 21:11 KorvinSzanto