Skip to content

Conversation

bgohla
Copy link

@bgohla bgohla commented Jul 11, 2024

I add a module Hints.NoCapitalisms that provides a hint to avoid capitalisms in identifiers, with some exceptions. E.g.

class FOO a where
class Foo a where getFOO
data Foo = Bar | BAAZ
getSMS

are discouraged.

The hint has severity Ignored, so it is off by default.

  • Things that might need attention
  • potentially factor out some code.
  • break into several commits.

a:b:c:as -> [a,b,c] : trigrams (c:as)
_otherwise -> []

--- these are copied from Hint.Naming ---
Copy link
Author

@bgohla bgohla Jul 11, 2024

Choose a reason for hiding this comment

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

If this PR goes ahead, I would propose factoring out the code below.

{-
Detect uses of capitalisms

Only allow up to two consecutive capital letters in identifiers.

Choose a reason for hiding this comment

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

What is the reason for this exception? ID? Personally I'd prefer uniformity here.

Copy link
Author

@bgohla bgohla Sep 13, 2024

Choose a reason for hiding this comment

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

That would be one case.

I'm all for uniformity, by I'm afraid not allowing this exception could make this rule very annoying. Let me do some statistics on our internal code base…

Copy link
Author

Choose a reason for hiding this comment

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

Actually, prohibiting two consecutive capitals would catch an identifier like MVar, I think that would be awkward.

I agree, we should favor the spelling Id over ID, but I can't think of a good rule here, that doesn't also catch, e.g., getIData.

Maybe we could tighten the proposed rule to prohibit two consecutive capitals unless followed by a lower case letter.

Copy link

Choose a reason for hiding this comment

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

This is a good point. There are many violations in the ecosystem that are utterly entrenched. Even if you try to be clever, for instance, allowing two capitals followed by lower-case / end of string, users will still need to do something about e.g. STM, TBQueue. There is no principled way to say the latter two are fine, but HTTP is not.

Hence I think the ideal solution would be to trigger on all consecutive capitals (no special rules), and allow users to easily disable specific cases globally. Can warnings be parameterized in this way? The best I can think of is something like:

# .hlint.yaml
- ignore:
    name: "No Capitalism"
    within:
      # I think this could work for functions.
      - "Module.readTBQueue"
      # Does this work for type / data declarations? Or does this assume
      # module Another/TBQueue.hs?
      - "Another.TBQueue"

This doesn't get you a global rule for ignoring e.g. IO, but maybe it's workable enough.

Choose a reason for hiding this comment

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

I thought this PR only complains about local definitions, not all occurrences of such names 🤔 Doesn't it? If it does, then definitions from other packages are not a problem.

Copy link

Choose a reason for hiding this comment

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

You are correct, though I am thinking of interop with upstream definitions, where re-using a captialized name is arguably more readable than adhering to this rule. For instance, I imagine quite a few people (most?) would say runInIO is better than runInIo, even for new definitions, because of how familiar IO is.

Copy link
Author

@bgohla bgohla Jun 13, 2025

Choose a reason for hiding this comment

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

I thought this PR only complains about local definitions, not all occurrences of such names 🤔 Doesn't it? If it does, then definitions from other packages are not a problem.

This rule only checks top-level definitions of value, constructor, type and class identifiers; it does not check occurrences, be they imported or not.

Copy link
Author

@bgohla bgohla Jun 13, 2025

Choose a reason for hiding this comment

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

# .hlint.yaml
- ignore:
    name: "No Capitalism"
    within:
      # I think this could work for functions.
      - "Module.readTBQueue"
      # Does this work for type / data declarations? Or does this assume
      # module Another/TBQueue.hs?
      - "Another.TBQueue"

This doesn't get you a global rule for ignoring e.g. IO, but maybe it's workable enough.

I don't understand the configuration language well enough to be able to confirm that your suggestion works. OTOH, ANN pragmas might be more effective for defining exceptions.

Copy link
Author

Choose a reason for hiding this comment

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

@arybczak I have become convinced that the stricter rule is feasible, i.e., prohibiting all consecutive capitals, exceptions have to be allowed using pragmas.

</TEST>
-}

module Hint.NoCapitalisms(noCapitalismsHint) where
Copy link
Collaborator

Choose a reason for hiding this comment

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

"capitalism"? How about "NoCAPs"?

Copy link
Author

Choose a reason for hiding this comment

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

Capitalism, as in, an expression written in all caps.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I do not see such a meaning for capitalism in the dictionary.

Choose a reason for hiding this comment

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

Yeah, "NoCAPs" seems better since it doesn't have political connotations and shouldn't provoke unnecessary discussions, unlike the current name.

import GHC.Util

noCapitalismsHint :: DeclHint
noCapitalismsHint _ _ decl = [ remark Ignore "Avoid capitalisms" (reLoc (shorten decl))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add a Note to the hint, explaining what it does?

Copy link
Author

Choose a reason for hiding this comment

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

Isn't the description at beginning of the file sufficient? I followed the style of Hint.Naming.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That's just a code comment. A Note is attached to a hint and is displayed with the hint. Alternatively, you can make the hint name more descriptive: "Avoid three consecutive capital letters" instead of "Avoid capitalisms".


--- these are copied from Hint.Naming ---

shorten :: LHsDecl GhcPs -> LHsDecl GhcPs
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add some comments explaining what this does.

Copy link
Author

Choose a reason for hiding this comment

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

done.

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

Successfully merging this pull request may close these issues.

5 participants