SiriusStarr / elm-review-no-unsorted / NoUnsortedLetDeclarations

Review Rule

rule : RuleConfig r -> Review.Rule.Rule

Reports let declarations that are not in the "proper" order.

🔧 Running with --fix will automatically sort the declarations.

The proper order of declarations is specified in the rule configuration. See the Configuration section below for more information.

config =
    [ NoUnsortedLetDeclarations.rule
        (NoUnsortedLetDeclarations.sortLetDeclarations
            |> NoUnsortedLetDeclarations.usedInExpressionFirst
            |> NoUnsortedLetDeclarations.alphabetically
        )
    ]

Fail

a =
    let
        -- These are used in the expression
        x =
            a

        y =
            b

        -- These are not
        b =
            j

        a =
            i
    in
    x + y

b =
    let
        -- These are not used in the expression
        a =
            i

        b =
            j

        -- These are
        x =
            a

        y =
            b
    in
    x + y

Success

a =
    let
        -- These are used in the expression
        x =
            a

        y =
            b

        -- These are not
        a =
            i

        b =
            j
    in
    x + y

When (not) to enable this rule

This rule is useful when you want to ensure that your let declarations are in a consistent, predictable order.

This rule is not useful when you want to be able to write let declarations in varying orders throughout your codebase, e.g. if you want to emphasize what is most important on a case-by-case basis.

Try it out

You can try this rule out by running the following command:

elm-review --template SiriusStarr/elm-review-no-unsorted/example --rules NoUnsortedLetDeclarations

Configuration


type RuleConfig r

Configuration for this rule. Create a new one with sortLetDeclarations and use orderings to create a hierarchy of sorting.

sortLetDeclarations : RuleConfig { noAlphabetical : (), noArgCount : (), noDependency : (), noHelper : (), noUsedInOther : (), noUsedInExpression : () }

Create a new RuleConfig. Use the various orderings to then specify primary and fallback orderings.

Orderings

alphabetically : RuleConfig { r | noAlphabetical : () } -> RuleConfig r

Sort declarations alphabetically by the name of their binding. For destructurings, this will be the name of the actual bindings that are made, in alphabetical order. For example, the following is sorted alphabetically:

let
    (Opaque a) =
        i

    ( z, b ) =
        j

    { c, y } =
        k

    d =
        l
in
x

usedInExpressionFirst : RuleConfig { r | noUsedInExpression : () } -> RuleConfig r

Sort declarations with those used in the expression of the let block coming first, then those that aren't. Ties will be broken by the next specified ordering. For example, the following is sorted by this ordering and then alphabetically:

let
    -- These are used in the expression
    x =
        a

    y =
        b

    -- These are not
    a =
        i

    b =
        j
in
x + y

usedInExpressionLast : RuleConfig { r | noUsedInExpression : () } -> RuleConfig r

Sort declarations with those used in the expression of the let block coming last, with those that aren't coming first. Ties will be broken by the next specified ordering. For example, the following is sorted by this ordering and then alphabetically:

let
    -- These are not used in the expression
    x =
        i

    y =
        j

    -- These are used in the expression
    a =
        x

    b =
        y
in
a + b

usedInOtherDeclarationsLast : RuleConfig { r | noUsedInOther : () } -> RuleConfig r

Sort declarations with those used in other declarations coming after those that are not. Ties will be broken by the next specified ordering. For example, the following is sorted by this ordering and then alphabetically:

let
    a =
        x

    b =
        y

    x =
        i

    y =
        j
in
0

usedInOtherDeclarationsFirst : RuleConfig { r | noUsedInOther : () } -> RuleConfig r

Sort declarations with those used in other declarations coming before those that are not. Ties will be broken by the next specified ordering. For example, the following is sorted by this ordering and then alphabetically:

let
    x =
        i

    y =
        j

    a =
        x

    b =
        y
in
0

valuesBeforeFunctions : RuleConfig { r | noArgCount : () } -> RuleConfig r

Sort declarations that do not have arguments before those that do. Since no type inference is performed, this does not guarantee that some things that are functions will not be sorted with values. For example, the following is sorted by this ordering and then alphabetically:

let
    -- These do not have arguments
    x =
        a

    y =
        b

    -- These do
    a i =
        i

    b j =
        j
in
x + y

valuesAfterFunctions : RuleConfig { r | noArgCount : () } -> RuleConfig r

Sort declarations that do not have arguments after those that do. Since no type inference is performed, this does not guarantee that some things that are functions will not be sorted with values. For example, the following is sorted by this ordering and then alphabetically:

let
    -- These have arguments
    a i =
        i

    b j =
        j

    -- These do not
    x =
        a

    y =
        b
in
x + y

Glues

Glues provide a way to "stick" one declaration to another, i.e. to always sort one declaration alongside another. Note that glues will chain, i.e. if a is glued before b and b is glued after c, then the result will be c -> a -> b (sorted wherever c is sorted to). Glues behave in the following ways:

glueHelpersBefore : RuleConfig { r | noHelper : () } -> RuleConfig r

Helpers are declarations that are not used in the expression that are used in exactly one other declaration. This glue attaches them immediately before the declaration they are used in.

For example:

foo input =
    let
        step : Int -> Int -> Int
        step i acc =
            i + acc

        sum : Int
        sum =
            List.foldl step 0 input
    in
    sum + 1

glueHelpersAfter : RuleConfig { r | noHelper : () } -> RuleConfig r

Helpers are declarations that are not used in the expression that are used in exactly one other declaration. This glue attaches them immediately after the declaration they are used in.

For example:

foo input =
    let
        sum : Int
        sum =
            List.foldl step 0 input

        step : Int -> Int -> Int
        step i acc =
            i + acc
    in
    sum + 1

glueDependenciesBeforeFirstDependent : RuleConfig { r | noDependency : () } -> RuleConfig r

Dependencies are declarations that are not used in the expression that are used in multiple other declarations. This glue attaches them immediately before the first declaration they are used in.

For example:

foo =
    let
        unwrap =
            some func

        a x =
            unwrap x

        b x =
            unwrap x

        c x =
            unwrap x
    in
    bar

glueDependenciesAfterFirstDependent : RuleConfig { r | noDependency : () } -> RuleConfig r

Dependencies are declarations that are not used in the expression that are used in multiple other declarations. This glue attaches them immediately after the first declaration they are used in.

For example:

foo =
    let
        a x =
            unwrap x

        unwrap =
            some func

        b x =
            unwrap x

        c x =
            unwrap x
    in
    bar

glueDependenciesBeforeLastDependent : RuleConfig { r | noDependency : () } -> RuleConfig r

Dependencies are declarations that are not used in the expression that are used in multiple other declarations. This glue attaches them immediately before the last declaration they are used in.

For example:

foo =
    let
        a x =
            unwrap x

        b x =
            unwrap x

        unwrap =
            some func

        c x =
            unwrap x
    in
    bar

glueDependenciesAfterLastDependent : RuleConfig { r | noDependency : () } -> RuleConfig r

Dependencies are declarations that are not used in the expression that are used in multiple other declarations. This glue attaches them immediately after the last declaration they are used in.

For example:

foo =
    let
        a x =
            unwrap x

        b x =
            unwrap x

        c x =
            unwrap x

        unwrap =
            some func
    in
    bar