Skip to content

lue-bird/elm-review-equals-caseable

Repository files navigation

Provides the elm-review rule 🔧EqualsCaseable.forbid which reports when == is used but there's an equivalent case of available.

reported

a =
    if list == [] then
        "empty"

    else
        "filled"

not reported

a =
    case list of
        [] ->
            "empty"

        _ :: _ ->
            "filled"

why

Reasons are pretty similar to VariablesBetweenCaseOf.AccessInCases.forbid. Let me highlight the most important ones:

(Note: The exhaustiveness benefits shown are possible once NoWildcard is implemented.)

notifies you when adding a new variant

type Role
    = Admin
    | User

canBanUser =
    \theUserWhoTriesToBan -> theUserWhoTriesToBan.role == Admin

It's a new day and time for a new feature: moderators!

type Role
    = Admin
    | Moderator
    | User

It compiles, so it works! Time for a break.

Oh crap! It don't work I'm sad elm has failed me I'm going back to typescript.

Oh no, you made them sad.

With the rules enabled:

type Role
    = Admin
    | User

canBanUser =
    \theUserWhoTriesToBan -> theUserWhoTriesToBan.role == Admin

EqualsCaseable.forbid Everywhere fixes to

canBanUser =
    \theUserWhoTriesToBan ->
        case theUserWhoTriesToBan.role of
            Admin ->
                True

            _ ->
                False

NoWildcard.rule fixes to

canBanUser =
    \theUserWhoTriesToBan ->
        case theUserWhoTriesToBan.role of
            Admin ->
                True

            User ->
                False

Now if you add moderator, elm will show you where there's more to handle specifically for moderators. Or as a quote by @janiczek on slack

Now the compiler will tell me all the places I need to make a new decision

gives the compiler as much information as you have

Here's a real example

toVariant pattern =
    if ... pattern.ending == Nothing then
        Elm.variant "Index"

    else
        let
            allSegments =
                ...
                    ++ ([ Maybe.map endingToVariantName pattern.ending
                        ]
                            |> List.filterMap identity
                       )
        in
        ...

Don't do this to yourself. Let the compiler know as much as you about the path you're in.

toVariant pattern =
    case pattern.ending of
        Nothing ->
            Elm.variant "Index"

        Just patternEnding ->
            let
                allSegments =
                    ... ++ [ endingToVariantName patternEnding ]
            in
            ...

doesn't make you jump around

Here's a real example

if fields.method == Form.Get then
    model.key
        |> Maybe.map
            (\key ->
                Browser.Navigation.pushUrl key
                    (appendFormQueryParams fields)
            )
        |> Maybe.withDefault
            Cmd.none

else
    -- wait, ... What is else?
    let
        urlToSubmitTo : Url
        urlToSubmitTo =
            model.url
    in
    fetchRouteData -1
        (UpdateCacheAndUrlNew False model.url Nothing)
        config
        urlToSubmitTo
        (Just fields)

be explicit :)

case fields.method of
    Form.Get ->
        model.key
            |> Maybe.map
                (\key ->
                    Browser.Navigation.pushUrl key
                        (appendFormQueryParams fields)
                )
            |> Maybe.withDefault
                Cmd.none

    Form.Post ->
        let
            urlToSubmitTo : Url
            urlToSubmitTo =
                model.url
        in
        fetchRouteData -1
            (UpdateCacheAndUrlNew False model.url Nothing)
            config
            urlToSubmitTo
            (Just fields)

try it out

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

elm-review --template lue-bird/elm-review-equals-caseable/example

configure

module ReviewConfig exposing (config)

import EqualsCaseable
import Review.Rule exposing (Rule)

config : List Rule
config =
    [ EqualsCaseable.forbid EqualsCaseable.InIf
    ]