rule : Basics.Int -> Review.Rule.Rule
Reports functions that have a too high cognitive complexity.
You can configure the threshold above which a function will be reported (15
in the example configuration below).
config =
[ CognitiveComplexity.rule 15
]
Cognitive complexity is not to be confused with "Cyclomatic Complexity", which has a different way of measuring the complexity.
Here's an explanation extracted from free white paper provided by SonarSource, the creators of the concept.
Cognitive complexity tries to measure how hard it is to understand a function, primarily focusing on the control structures that hinder the understanding of a function by reading it from top to bottom in one go, like you would for a novel.
A Cognitive Complexity score is assessed according to three basic rules:
- Ignore structures that allow multiple statements to be readably shorthanded into one
- Increment (add one) for each break in the linear flow of the code
- Increment when flow-breaking structures are nested
Some small differences may be found between the implementation detailed in the paper and this rule, as the idea was formulated more on imperative programming languages, and may not be applicable to a pure functional language like Elm.
You can read about how is works in the complexity breakdown section below.
This rule is an experiment. I don't know if this will be more useful or detrimental, and I haven't yet figured out what the ideal complexity threshold for Elm projects is.
I would for now recommend to use it with a very high threshold to find places in your codebase that need refactoring, and eventually to enable it in your configuration to make sure no new extremely complex functions appear. As you refactor more and more of your codebase, you can gradually lower the threshold until you reach a level that you feel happy with.
Please let me know how enabling this rule works out for you! If enforcing doesn't work for you, then you can use this as an insight rule instead.
If instead of enforcing a threshold, you wish to have an overview of the complexity for each function, you can run the
rule as an insight rule (using elm-review --report=json --extract
), which would yield an output like the following:
{
"Some.Module": {
"someFunction": 16,
"someOtherFunction": 0
},
"Some.Other.Module": {
"awesomeFunction": 2
}
}
Following is a breakdown of how the complexity of a function is computed:
else if
also increases complexity by 1 + nesting, but doesn't further increase the nesting.
else
doesn't increase the complexity.-- Total: 4
a =
if b then -- +1
if c then -- +2, including 1 for nesting
1
else
2
else if d then -- +1
3
else -- +0
4
-- Total: 3
a =
case b of -- +1
A -> 1
B -> 2
C ->
case c of -- +2, including 1 for nesting
_ -> 3
D -> 4
-- Total: 2
a =
let
fn b = -- increases nesting
if b then -- +2, including 1 for nesting
1
else
2
constant = -- Not a function, no increase
True
in
fn constant
-- Total: 2
a things =
List.map
(\thing -> -- increases nesting
case thing of -- +2, including 1 for nesting
Just _ -> 1
Nothing -> 2
)
things
a && b -- +1
-- This is still the same logical construction as
-- above, and therefore about as hard to understand
a && b && c && d && e -- +1
-- Total: 3
a && b && c -- +1
|| d -- +1 for breaking the chain of && with a ||
|| not (e || f) -- +1 for breaking the chain
-- with a `not` of a binary operation
-- Total: 2
fun1 n =
fun2 n -- +1
+ fun2 n -- +0, already counted
+ fun1 n -- +1
-- Total: 1
fun2 n =
fun1 n -- +1
The original metric increases the complexity for other structures that the Elm language doesn't have.
You can try this rule out by running the following command:
elm-review --template jfmengels/elm-review-cognitive-complexity/example --rules CognitiveComplexity
The cognitive complexity is set to 15 in the configuration used by the example.
If instead of enforcing a threshold, you wish to have an overview of the complexity for each function, you can run the
rule like this (requires jq
):
elm-review --template jfmengels/elm-review-cognitive-complexity/example --extract --report=json --rules CognitiveComplexity | jq -r '.extracts.CognitiveComplexity'
Thanks to the team at SonarSource for designing the metric and for not restricting its use. Thanks to G. Ann Campbell for the different talks she made on the subject.