It is recommended that you also include
NoUnused.Patterns
in your config, as the fixes for this rule can sometimes generate nested as
patterns in very complex cases (e.g. when nested single-pattern cases are
simplified). These cases cannot be resolved automatically, as it is unclear
which name should be preferred.
Configure the rule, determining how automatic fixes are generated.
The default Configs fixInArgument and
fixInLet should be used as reasonable defaults, with more
customization detailed in those sections.
The behavior of the rule with constructors that don't match their type name can
be configured via reportAllCustomTypes. By default,
only constructors with an identical name to their type are reported.
The behavior of the rule in the context of useless single pattern cases can also
be configured via replaceUnusedBindings. A single
pattern case is considered to be useless if its pattern does not bind any name
that is actually used in the expression.
Always fix by destructuring in the argument. This will use as patterns if
necessary. If the argument cannot be destructured in, no fix will be generated.
For example:
f1 o =
case o of
Opaque i ->
i
f2 o =
let
x =
someFunc o
in
case o of
Opaque i ->
i + x
f3 { recordField } =
case recordField of
Opaque i ->
iwill be fixed to:
f1 (Opaque i) =
i
f2 ((Opaque i) as o) =
let
x =
someFunc o
in
i + x
f3 { recordField } =
case recordField of
Opaque i ->
iUse ifAsPatternRequired and
ifCannotDestructureAtArgument to customize
the behavior in either of these cases.
Always fix by destructuring in a let block, creating a new one if none
exists.
For example:
f1 o =
case o of
Opaque i ->
i
f2 o =
let
x =
someFunc o
in
case o of
Opaque i ->
i + xwill be fixed to:
f1 o =
let
(Opaque i) =
o
in
i
f2 ((Opaque i) as o) =
let
x =
someFunc o
(Opaque i) =
o
in
i + xUse ifNoLetExists to customize the behavior in the case
where no let block exists within the scope the pattern is valid in. To
clarify, the following counts as no let existing:
unpack : Opaque -> Int
unpack o =
let
foo =
bar
in
(\a ->
case a of
Opaque i ->
i
)
oas the name a is not in scope in the extant let block.
ifNoLetExists also handles the case where the closest let
block would result in a name clash. To clarify, the following counts as no
let existing:
unpack : Opaque -> Int
unpack o =
let
foo =
(\i -> i + 1) 0
in
case o of
Opaque i ->
ias i cannot be unpacked in the let block, as doing so would cause a name
clash with the i in foo.
By default, only constructors whose names are identical to their type are
reported, along with types imported from dependencies (since those aren't
expected to be iterated on). This setting changes behavior back to that of
version 2.0.2 and earlier, where absolutely all single pattern cases are
flagged by the rule, regardless of the types.
-- import the constructor `OutsideConstructor` from some other package
import SomeOutsidePackage exposing (OutsideType(..))
type Date
= -- Constructor has same name as type
Date Int
type Msg
= -- Constructor has different name than type
ThingieClicked
update1 : Date -> Int -> Int
update1 date i =
case date of
-- THIS CASE IS ALWAYS FLAGGED
Date j ->
i + j
update2 : Msg -> Int -> Int
update2 msg i =
case msg of
-- THIS CASE IS NOT FLAGGED BY DEFAULT, IS FLAGGED WITH `reportAllCustomTypes`
ThingieClicked ->
i + 1
update3 : OutsideType -> Int -> Int
update3 oType i =
case oType of
-- THIS CASE IS ALWAYS FLAGGED
OutsideConstructor j ->
i + jA single pattern case is considered to be useless if its pattern does not bind any name that is actually used in the expression, e.g.
case x of
_ ->
True
case x of
() ->
True
case x of
A ({ field1, field2 } as record) ->
List.map foo bar
|> List.sum
|> bazThe rule will always provide fixes for such cases but by default will not
replace the binding used in the case...of expression. This option configures
the rule to replace such bindings where possible with the most specific option.
For example:
f unusedArg =
case unusedArg of
_ ->
Truewill be fixed to
f _ =
Trueand
f x =
case x of
A ({ field1, field2 } as record) ->
List.map foo bar
|> List.sum
|> bazwill be fixed to
f (A _) =
List.map foo bar
|> List.sum
|> bazThis provides a more clear indication that the binding is unused. The binding
will of course not be replaced if it used anywhere but in case...of.
This setting changes behavior back to that of version 2.0.2 and earlier,
where useless bindings are replaced by _ (or ()), not by the constructor
name.
For example,
f x =
case x of
A ({ field1, field2 } as record) ->
List.map foo bar
|> List.sum
|> bazwill be fixed to
f _ =
List.map foo bar
|> List.sum
|> bazThis is useful if you are reliant on an IDE that doesn't recognize
pointless CreateNewLet =
fooas valid Elm syntax. The downside is that you will not receive compiler warnings if the argument changes (e.g. if you add a new constructor to the type).
Specify what to do if an as pattern would be required to destructure in
the argument, e.g.
f o =
let
x =
someFunc o
in
case o of
Opaque i ->
i + xAvailable options are useAsPattern (this is the default),
fixInLetInstead or fail,
e.g.
c1 =
fixInArgument
|> ifAsPatternRequired fail
c2 =
fixInArgument
|> ifAsPatternRequired useAsPattern
c3 =
fixInArgument
|> ifAsPatternRequired
(fixInLetInstead
|> andIfNoLetExists createNewLet
)
c4 =
fixInArgument
|> ifAsPatternRequired
(fixInLetInstead
|> andIfNoLetExists useAsPattern
)Specify what to do if the argument cannot be destructured at, either due to it being a record field, e.g.
f { recordField } =
case recordField of
Opaque i ->
ior a more complex case expression, e.g.
f a =
case foo <| bar a of
Opaque i ->
ior due to a name clash that would be caused by the increase in scope, e.g.
unpack : Opaque -> Int
unpack o =
let
foo =
(\i -> i + 1) 0
in
case o of
Opaque i ->
iAvailable options are fixInLetInstead or fail
(this is the default).
c1 =
fixInArgument
|> ifCannotDestructureAtArgument fail
c2 =
fixInArgument
|> ifCannotDestructureAtArgument
(fixInLetInstead
|> andIfNoLetExists fail
)
c3 =
fixInArgument
|> ifCannotDestructureAtArgument
(fixInLetInstead
|> andIfNoLetExists createNewLet
)Specify what to do it no let block exists in scope, instead of creating a
new one.
Available options are fixInArgumentInstead,
createNewLet (this is the default), or fail. Note
that andIfAsPatternRequired and
andIfCannotDestructureAtArgument must appear
in that order after fixInArgumentInstead.
c1 =
fixInLet
|> ifNoLetExists fail
c2 =
fixInLet
-- This is the default
|> ifNoLetExists createNewLet
c3 =
fixInLet
|> ifNoLetExists
(fixInArgumentInstead
|> andIfAsPatternRequired useAsPattern
|> andIfCannotDestructureAtArgument fail
)These functions are simply used by
ifAsPatternRequired,
ifCannotDestructureAtArgument, and
ifNoLetExists to customize behavior of the default configs.
Look at the examples in those to understand how to use them.
Choose to create a let block when none exists.
Choose to use an as pattern to destructure in the argument if necessary.
Fallback to destructuring in the argument instead of a let block.
Note that andIfAsPatternRequired and
andIfCannotDestructureAtArgument must appear
in that order, e.g.
c =
fixInLet
|> ifNoLetExists
(fixInArgumentInstead
|> andIfAsPatternRequired useAsPattern
|> andIfCannotDestructureAtArgument fail
)Specify what to do if an as pattern would be necessary.
Available options are useAsPattern,
createNewLet or fail
c1 =
fixInLet
|> ifNoLetExists
(fixInArgumentInstead
|> andIfAsPatternRequired useAsPattern
|> andIfCannotDestructureAtArgument fail
)
c2 =
fixInLet
|> ifNoLetExists
(fixInArgumentInstead
|> andIfAsPatternRequired createNewLet
|> andIfCannotDestructureAtArgument fail
)
c3 =
fixInLet
|> ifNoLetExists
(fixInArgumentInstead
|> andIfAsPatternRequired fail
|> andIfCannotDestructureAtArgument fail
)Specify what to do if the argument cannot be destructured at, e.g.
f { recordField } =
case recordField of
Opaque i ->
iAvailable options are createNewLet or fail
c1 =
fixInLet
|> ifNoLetExists
(fixInArgumentInstead
|> andIfAsPatternRequired fail
|> andIfCannotDestructureAtArgument createNewLet
)
c2 =
fixInLet
|> ifNoLetExists
(fixInArgumentInstead
|> andIfAsPatternRequired fail
|> andIfCannotDestructureAtArgument fail
)Fallback to destructuring in a let block instead of the argument.
If no let block exists to destructure in, choose some other behavior
instead.
You shouldn't need to worry about these types; they are exported solely for the sake of annotation, should it be necessary.
Phantom type for Config fixBy.
Phantom type for Config fixBy.
Fallback to destructuring in the argument, if choosing not to create a let
block.
Fallback to using a let block if destructuring in the argument has failed
(or we've chosen not to).
Choose to create a new let block to destructure in.
Choose to fail at generating a fix.
Choose to use an as pattern.
At this point, an as pattern could be used or some other option.
At this point, a new let could be created or some other option.
At this point, the argument could be used or a new let block created.
block.
At this point, a let could be used or either of two other options.
At this point, the only option is to use a let block.
At this point, an as pattern could be used, or we could use a let block.
Offer a choice between two options.
Reports single-pattern case expressions, which may be written more concisely or removed entirely.
See
Configfor configuration details.Fails
Single-pattern case expressions for destructuring are not allowed, as:
may instead be written more concisely, for example as
Similarly, single-pattern case expressions that ase not used for destructuring are not allowed, as:
may instead be written more concisely, for example as
or
Success
Single patterns with constructors that do not match their type name, e.g.
type Msg = ButtonClicked, are allowed by default, unless they are imported from dependencies (as those types are not expected to be iterated on). This behavior can be changed withreportAllCustomTypes.Any case expression with more than one pattern match will not be reported. Consider using
jfmengels/elm-review-simplifyto detect unnecessary multi-pattern cases.When (not) to enable this rule
This rule is useful if you prefer destructuring in function/lambda arguments or
letbindings, rather than in a single-pattern case.This rule is not useful if you prefer the more verbose style.
Try it out
You can try this rule out by running the following command: