Code Monkey home page Code Monkey logo

elm-review-cognitive-complexity's Introduction

elm-review-cognitive-complexity

Provides an elm-review rule to measure the cognitive complexity of a function.

Provided rules

Configuration

module ReviewConfig exposing (config)

import CognitiveComplexity
import Review.Rule exposing (Rule)

config : List Rule
config =
    [ CognitiveComplexity.rule 15
    ]

Try it out

You can try the example configuration above out by running the following command:

elm-review --template jfmengels/elm-review-cognitive-complexity/preview

elm-review-cognitive-complexity's People

Contributors

jfmengels avatar siriusstarr avatar

Stargazers

Adrian Sieber avatar  avatar Decio Battaglia avatar Ruben Lie King avatar

Watchers

James Cloos avatar  avatar  avatar

Forkers

martinsstewart

elm-review-cognitive-complexity's Issues

Cognitive Complexity and Elm Pattern Matching

First of all, thank you for creating this rule! In my past JavaScript life, I spent a lot of time writing different custom ESLint rules with different approaches toward measuring code complexity. I'd not heard of Cognitive Complexity until your blog post about this rule.

After watching the YouTube presentations from G. Ann Campbell, I think the motivating imperative-language concepts mostly map pretty well to a functional language like Elm. But as I tried out this rule in my own Elm code, I started to think that certain kinds of pattern matching in case expressions needed to be treated differently.

Your blog post explains how the rule treats case expressions:

A case expression will only increment the complexity by 1, regardless of how many branches it has.

This comes from the imperative switch/case statement examples motivating the original creators. In those situations, the order of the different case conditions doesn't matter from a logic perspective (only potentially from a performance perspective), so it makes sense not to increment the complexity for each case.

However, pattern matching in Elm can sometimes have logic that is dependent on the order of the case conditions, just like how the order of if/else if conditions can also be important. In those situations, I think the case conditions ought to be scored the same as else if conditions would be.

Below is a simplified example from the code for a game I'm writing, where I want to resume with a brief countdown after the player un-pauses the game:

updateGameMode : GameMode -> GameMode
updateGameMode gameMode =
    case gameMode of
        ResumePlaying 1 ->
            Playing
        ResumePlaying n ->
            ResumePlaying (n - 1)
        Pause ->
            ResumePlaying 3
        Playing ->
            Pause
        GameOver ->
            GameOver

The order of pattern matching for the different ResumePlaying conditions is significant โ€“ I have to match on the ResumePlaying 1 condition before the more general ResumePlaying n condition. And this is just a simple example โ€“ case expressions in Elm can have pattern matching far more complex than this.

In situations like this, I think each complex case condition needs to increase the overall Cognitive Complexity score the same way an else if condition would. The main challenge would be to arrive at the correct logic for identifying "complex" case conditions which ought to be scored this way.

Gradual adoption of the rule

I was thinking of changing how CognitiveComplexity (https://package.elm-lang.org/packages/jfmengels/elm-review-cognitive-complexity/latest/CognitiveComplexity) works.

At the moment, you set a threshold for the whole project, so you're bound to set it at highest value of all the functions, and then (maybe painfully?) lower it by making some functions simpler. But in the meantime, some of the functions in your files might become more complex unnoticeably up to that threshold.

I just added the rule to a codebase where the threshold has been set to 110, because there are some very large update functions in old modules that will take some time to simplify. This is a very large number, and I get the feeling that the rule will stay where it's at for a long time because the first obstacle would be annoying to solve. In the meantime, complexity in new or currently okay files would rise unnoticeably without the rule helping preventing it.

So my thinking is that we could have a configuration for the rule like this

module ReviewConfig exposing (config)

import CognitiveComplexity
import CognitiveComplexitySuppressions
import Review.Rule exposing (Rule)

config : List Rule
config =
    [ CognitiveComplexity.rule CognitiveComplexityConfiguration.config
    ]

and then have another file like this

module CognitiveComplexityConfiguration exposing (config)

{-| Generated by the CognitiveComplexity rule.

These suppressions are here to help you gradually lower the complexity of your modules.

-}

import CognitiveComplexity

config : CognitiveComplexity.Config
config =
    { threshold = 10
    , suppressions = suppressions
    }

suppressions : List ( String, Int )
suppressions =
    [ { moduleName = "Some.Module", threshold = 64 }
    , { moduleName = "Some.Other.Module", threshold = 14 }
    ]

My idea would be to have the rule generate this configuration when you configure it like CognitiveComplexity.generateComplexityCoverage { threshold = 10 }, in a global error like:

CognitiveComplexity: Here is the generated file. Please change your configuration to <what I showed above> and create a file with the following content:

<contents of the CognitiveComplexityConfiguration file above>

This would generate a list with all modules where the threshold is the complexity of the most complex function in that file. Modules with complexities under the global threshold would not be listed.

Then once you have re-configured the rule, the threshold in effect for a module is the one listed in suppressions (better name suggestions welcome) and the global threshold if not listed.

If no errors are being reported, but the maximum threshold for a module has changed to be lower than before, then you'd have a global error like the one above, asking you to copy and paste the contents inside CognitiveComplexityConfiguration.elm, so that end threshold becomes increasingly low.

What do you think of the idea?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.