Reports when an expression can be simplified.
🔧 Running with --fix will automatically remove all the reported errors.
config =
[ Simplify.rule Simplify.defaults
]Configuration for this rule. Create a new one with defaults and use ignoreCaseOfForTypes to alter it.
Default configuration for this rule.
The rule aims tries to improve the code through simplifications that don't impact the behavior. An exception to this are
when the presence of NaN values
Use expectNaN if you want to opt out of changes that can impact the behaviour of your code if you expect to work with NaN values.
Use ignoreCaseOfForTypes if you want to prevent simplifying case expressions that work on custom types defined in dependencies.
config =
[ Simplify.rule Simplify.defaults
]
-- or
config =
[ Simplify.defaults
|> Simplify.expectNaN
|> Simplify.ignoreCaseOfForTypes [ "Module.Name.Type" ]
|> Simplify.rule
]Usually, elm-review-simplify will only suggest simplifications that are safe to apply without risk of changing the original behavior.
However, when encountering NaN
values, some simplifications can actually impact behavior.
For instance, the following expression will evaluate to True:
x == x
--> TrueHowever, if x is NaN or a value containing NaN then the expression will evaluate to False:
-- given x = NaN
x == x
--> False
-- given x = { a = ( NaN, 0 ) }
x == x
--> FalseGiven the potential presence of NaN, some simplifications become unsafe to apply:
x == x to TrueList.member x [ x ] to Truen * 0 to 0This special value is hard to recreate in Elm code both intentionally and unintentionally, and it's therefore unlikely to be found in your application, which is why the rule applies these simplifications by defaults.
If you somehow expect to create and encounter NaN values in your codebase, then you can use this function to disable these simplifications altogether.
config =
[ Simplify.defaults
|> Simplify.expectNaN
|> Simplify.rule
]Ignore some reports about types from dependencies used in case expressions.
This rule simplifies the following construct:
module Module.Name exposing (..)
case value of
Just _ -> x
Nothing -> x
--> x(Since v2.0.19) it will not try to simplify the case expression when some of the patterns references custom types constructors
defined in the project. It will only do so for custom types that are defined in dependencies (including elm/core).
If you do happen to want to disable this simplification for a type Module.Name.Type, you can configure the rule like this:
config =
[ Simplify.defaults
|> Simplify.ignoreCaseOfForTypes [ "Module.Name.Type" ]
|> Simplify.rule
]I personally don't recommend to use this function too much, because this could be a sign of premature abstraction, and because I think that often You Aren't Gonna Need this code.
Please let me know by opening an issue if you do use this function, I am very curious to know;
You can try this rule out by running the following command:
elm-review --template jfmengels/elm-review-simplify/example --rules SimplifyBelow is the list of all kinds of simplifications this rule applies.
x || True
--> True
x || False
--> x
x && True
--> x
x && False
--> False
not True
--> False
not (not x)
--> x
-- for `<`, `>`, `<=`, `>=`, `==` and `/=`
not (a < b)
--> a >= bx == True
--> x
x /= False
--> x
not x == not y
--> x == y
anything == anything
--> True
anything /= anything
--> False
{ r | a = 1 } == { r | a = 2 }
--> Falseif True then x else y
--> x
if False then x else y
--> y
if condition then x else x
--> x
if condition then True else False
--> condition
if condition then False else True
--> not condition
a =
if condition then
if not condition then
1
else
2
else
3
--> if condition then 2 else 3case condition of
True -> x
False -> y
--> if condition then x else y
case condition of
False -> y
True -> x
--> if not condition then x else y
-- only when no variables are introduced in the pattern
-- and no custom types defined in the project are referenced
case value of
Just _ -> x
Nothing -> x
--> xDestructuring using case expressions
case value of
( x, y ) ->
x + y
-->
let
( x, y ) =
value
in
x + ylet
a =
1
in
let
b =
1
in
a + b
-->
let
a =
1
b =
1
in
a + b{ a | b = a.b }
--> a
{ a | b = a.b, c = 1 }
--> { a | c = 1 }{ a = b }.a
--> b
{ a | b = c }.b
--> c
{ a | b = c }.d
--> a.d
(let a = b in c).d
--> let a = b in c.didentity x
--> x
f >> identity
--> f
always x y
--> x
f >> always x
--> always x(\_ -> x) data
--> x
(\() y -> x) ()
--> (\y -> x)
(\_ y -> x) data
--> (\y -> x)(++) a b
--> a ++ b
a |> f >> g
--> a |> f |> gn + 0
--> n
n - 0
--> n
0 - n
--> -n
n * 1
--> n
0 // n
--> 0
n // 0
--> 0
n // 1
--> n
n / 1
--> n
0 / n
--> 0
-(-n)
--> n
negate (negate n)
--> n
n - n
--> 0Tuple.pair a b
--> ( a, b )
Tuple.first ( a, b )
--> a
Tuple.second ( a, b )
--> b"a" ++ ""
--> "a"
String.fromList []
--> ""
String.fromList [ a ]
--> String.fromChar a
String.fromList (String.toList str)
--> str
String.toList (String.fromList list)
--> list
String.isEmpty ""
--> True
String.isEmpty "a"
--> False
String.concat []
--> ""
String.append "" str
--> str
String.append (String.fromList [ a, b ]) (String.fromList [ c, d ])
--> String.fromList [ a, b, c, d ]
String.join str []
--> ""
String.join "" list
--> String.concat list
String.length "abc"
--> 3
String.repeat n ""
--> ""
String.repeat 0 str
--> ""
String.repeat 1 str
--> str
String.replace x y ""
--> ""
String.replace x x z
--> z
String.replace "x" "y" "z"
--> "z" -- only when resulting string is unchanged
String.words ""
--> []
String.lines ""
--> []
String.reverse ""
--> ""
String.reverse (String.fromChar a)
--> String.fromChar a
String.reverse (String.reverse str)
--> str
String.slice n n str
--> ""
String.slice n 0 str
--> ""
String.slice a z ""
--> ""
String.left 0 str
--> ""
String.left -1 str
--> ""
String.left n ""
--> ""
String.right 0 str
--> ""
String.right -1 str
--> ""
String.right n ""
--> ""
String.slice 2 1 str
--> ""
String.slice -1 -2 str
--> ""Maybe.map identity x
--> x
Maybe.map f Nothing
--> Nothing
Maybe.map f (Just x)
--> Just (f x)
Maybe.andThen f Nothing
--> Nothing
Maybe.andThen (always Nothing) x
--> Nothing
Maybe.andThen (\a -> Just b) x
--> Maybe.map (\a -> b) x
Maybe.andThen (\a -> if condition a then Just b else Just c) x
--> Maybe.map (\a -> if condition a then b else c) x
Maybe.andThen f (Just x)
--> f x
Maybe.withDefault x Nothing
--> x
Maybe.withDefault x (Just y)
--> yResult.map identity x
--> x
Result.map f (Err x)
--> Err x
Result.map f (Ok x)
--> Ok (f x)
-- the following simplifications for map3 work for all Result.mapN
Result.map3 f (Ok a) (Ok b) (Ok c)
--> Ok (f a b c)
Result.map3 f (Ok a) (Err x) thirdResult
--> Err x
Result.map3 f firstResult (Err x) thirdResult
--> Result.map2 f firstResult (Err x)
Result.mapError identity x
--> x
Result.mapError f (Ok x)
--> Ok x
Result.mapError f (Err x)
--> Err (f x)
Result.andThen f (Err x)
--> Err x
Result.andThen f (Ok x)
--> f x
Result.andThen (\a -> Ok b) x
--> Result.map (\a -> b) x
Result.withDefault x (Err y)
--> x
Result.withDefault x (Ok y)
--> y
Result.toMaybe (Ok x)
--> Just x
Result.toMaybe (Err e)
--> Nothinga :: []
--> [ a ]
a :: [ b ]
--> [ a, b ]
[ a ] ++ list
--> a :: list
[] ++ list
--> list
[ a, b ] ++ [ c ]
--> [ a, b, c ]
List.append [] ys
--> ys
List.append [ a, b ] [ c ]
--> [ a, b, c ]
List.head []
--> Nothing
List.head (a :: bToZ)
--> Just a
List.tail []
--> Nothing
List.tail (a :: bToZ)
--> Just bToZ
List.member a []
--> False
List.member a [ a, b, c ]
--> True
List.member a [ b ]
--> a == b
List.map f [] -- same for most List functions like List.filter, List.filterMap, ...
--> []
List.map identity list
--> list
List.map f [ a ]
--> [ f a ]
List.filter (always True) list
--> list
List.filter (always False) list
--> []
List.filterMap Just list
--> list
List.filterMap (\a -> if condition a then Just b else Just c) list
--> List.map (\a -> if condition a then b else c) list
List.filterMap (always Nothing) list
--> []
List.filterMap identity (List.map f list)
--> List.filterMap f list
List.filterMap identity [ Just x, Just y ]
--> [ x, y ]
List.filterMap identity [ a, Nothing, b ]
--> List.filterMap identity [ a, b ]
List.concat [ [ a, b ], [ c ] ]
--> [ a, b, c ]
List.concat [ a, [ 1 ], [ 2 ] ]
--> List.concat [ a, [ 1, 2 ] ]
List.concat [ a, [], b ]
--> List.concat [ a, b ]
List.concatMap identity list
--> List.concat list
List.concatMap (\a -> [ b ]) list
--> List.map (\a -> b) list
List.concatMap f [ x ]
--> f x
List.concatMap (always []) list
--> []
List.concat (List.map f list)
--> List.concatMap f list
List.indexedMap (\_ value -> f value) list
--> List.map (\value -> f value) list
List.intersperse a []
--> []
List.intersperse s [ a ]
--> [ a ]
List.isEmpty []
--> True
List.isEmpty [ a ]
--> False
List.isEmpty (x :: xs)
--> False
List.sum []
--> 0
List.sum [ a ]
--> a
List.product []
--> 1
List.product [ a ]
--> a
List.minimum []
--> Nothing
List.minimum [ a ]
--> Just a
List.maximum []
--> Nothing
List.maximum [ a ]
--> Just a
-- The following simplifications for List.foldl also work for List.foldr
List.foldl f x []
--> x
List.foldl (\_ soFar -> soFar) x list
--> x
List.foldl (+) 0 list
--> List.sum list
List.foldl (+) initial list
--> initial + List.sum list
List.foldl (*) 1 list
--> List.product list
List.foldl (*) 0 list
--> 0
List.foldl (*) initial list
--> initial * List.product list
List.foldl (&&) True list
--> List.all identity list
List.foldl (&&) False list
--> False
List.foldl (||) False list
--> List.any identity list
List.foldl (||) True list
--> True
List.all f []
--> True
List.all (always True) list
--> True
List.any f []
--> True
List.any (always False) list
--> False
List.any ((==) x) list
--> List.member x list
List.range 6 3
--> []
List.length [ a, b, c ]
--> 3
List.repeat 0 x
--> []
List.repeat 1 x
--> List.singleton x
List.partition f []
--> ( [], [] )
List.partition (always True) list
--> ( list, [] )
List.partition (always False) list
--> ( [], list )
List.take 0 list
--> []
List.drop 0 list
--> list
List.reverse []
--> []
List.reverse [ a ]
--> [ a ]
List.reverse (List.reverse list)
--> list
List.sortBy (always a) list
--> list
List.sortBy identity list
--> List.sort list
List.sortWith (\_ _ -> LT) list
--> List.reverse list
List.sortWith (\_ _ -> EQ) list
--> list
List.sortWith (\_ _ -> GT) list
--> list
-- The following simplifications for List.sort also work for List.sortBy f and List.sortWith f
List.sort []
--> []
List.sort [ a ]
--> [ a ]
-- same for up to List.map5 when any list is empty
List.map2 f xs []
--> []
List.map2 f [] ys
--> []
List.unzip []
--> ( [], [] )Array.fromList []
--> Array.empty
Array.fromList (Array.toList array)
--> array
Array.toList (Array.fromList list)
--> list
Array.map f Array.empty -- same for Array.filter
--> Array.empty
Array.map identity array
--> array
Array.indexedMap (\_ value -> f value) array
--> Array.map (\value -> f value) array
Array.isEmpty Array.empty
--> True
Array.repeat 0 x
--> Array.empty
Array.initialize 0 f
--> Array.empty
Array.length Array.empty
--> 0
Array.length (Array.fromList [ a, b, c ])
--> 3
Array.length (Array.repeat 3 x)
--> 3
Array.length (Array.initialize 3 f)
--> 3
Array.length (Array.repeat n x)
--> max 0 n
Array.length (Array.initialize n f)
--> max 0 n
Array.append Array.empty array
--> array
Array.append (Array.fromList [ a, b ]) (Array.fromList [ c, d ])
--> Array.fromList [ a, b, c, d ]
Array.get n Array.empty
--> Nothing
Array.get 1 (Array.fromList [ a, b, c ])
--> Just b
Array.get 100 (Array.fromList [ a, b, c ])
--> Nothing
Array.get -1 array
--> Nothing
Array.set n x Array.empty
--> Array.empty
Array.set -1 x array
--> array
Array.set 1 x (Array.fromList [ a, b, c ])
--> Array.fromList [ a, x, c ]
Array.set 100 x (Array.fromList [ a, b, c ])
--> Array.fromList [ a, b, c ]Set.fromList []
--> Set.empty
Set.fromList [ a ]
--> Set.singleton a
Set.fromList (Set.toList set)
--> set
Set.map f Set.empty -- same for Set.filter, Set.remove...
--> Set.empty
Set.map identity set
--> set
Set.isEmpty Set.empty
--> True
Set.member x Set.empty
--> False
Set.toList Set.empty
--> []
Set.length Set.empty
--> 0
Set.intersect Set.empty set
--> Set.empty
Set.diff Set.empty set
--> Set.empty
Set.diff set Set.empty
--> set
Set.union set Set.empty
--> set
Set.union (Set.fromList [ a, b ]) (Set.fromList [ c, d ])
--> Set.fromList [ a, b, c, d]
Set.insert x Set.empty
--> Set.singleton x
-- same for foldr
List.foldl f x (Set.toList set)
--> Set.foldl f x set
Set.partition f Set.empty
--> ( Set.empty, Set.empty )
Set.partition (always True) set
--> ( set, Set.empty )Dict.fromList []
--> Dict.empty
Dict.fromList (Dict.toList dict)
--> dict
Dict.isEmpty Dict.empty
--> True
Dict.toList Dict.empty
--> []
Dict.size Dict.empty
--> 0
Dict.member x Dict.empty
--> False
Dict.intersect Dict.empty dict
--> Dict.empty
Dict.diff Dict.empty dict
--> Dict.empty
Dict.diff dict Dict.empty
--> dict
Dict.union dict Dict.empty
--> dict
Dict.union (Dict.fromList [ a, b ]) (Dict.fromList [ c, d ])
--> Dict.fromList [ c, d, a, b ]
Dict.partition f Dict.empty
--> ( Dict.empty, Dict.empty )
Dict.partition (always True) dict
--> ( dict, Dict.empty )
Dict.partition (always False) dict
--> ( Dict.empty, dict )
List.map Tuple.first (Dict.toList dict)
--> Dict.keys dict
List.map Tuple.second (Dict.toList dict)
--> Dict.values dictAll of these also apply for Sub.
Cmd.batch []
--> Cmd.none
Cmd.batch [ a ]
--> a
Cmd.batch [ a, Cmd.none, b ]
--> Cmd.batch [ a, b ]
Cmd.map identity cmd
--> cmd
Cmd.map f Cmd.none
--> Cmd.noneTask.map identity task
--> task
Task.map f (Task.fail x)
--> Task.fail x
Task.map f (Task.succeed a)
--> Task.succeed (f a)
-- the following simplifications for map3 work for all Task.mapN
Task.map3 f (Task.succeed a) (Task.succeed b) (Task.succeed c)
--> Task.succeed (f a b c)
Task.map3 f (Task.succeed a) (Task.fail x) thirdTask
--> Task.fail x
Task.map3 f firstTask (Task.fail x) thirdTask
--> Task.map2 f firstTask (Task.fail x)
Task.andThen f (Task.fail x)
--> Task.fail x
Task.andThen f (Task.succeed a)
--> f a
Task.andThen Task.succeed task
--> task
Task.andThen (\a -> Task.succeed b) task
--> Task.map (\a -> b) x
Task.mapError identity task
--> task
Task.mapError f (Task.succeed a)
--> Task.succeed a
Task.mapError f (Task.fail x)
--> Task.fail (f x)
Task.onError f (Task.succeed a)
--> Task.succeed a
Task.onError f (Task.fail x)
--> f x
Task.onError Task.fail task
--> task
Task.onError (\x -> Task.fail y) task
--> Task.mapError (\x -> y) x
Task.sequence [ Task.succeed a, Task.succeed b ]
--> Task.succeed [ a, b ]
Task.sequence [ Task.succeed a, Task.fail x ]
--> Task.fail x
Task.sequence [ a, Task.fail x, b ]
--> Task.sequence [ a, Task.fail x ]
Task.sequence [ task ]
--> Task.map List.singleton taskHtml.Attributes.classList [ x, y, ( z, False ) ]
--> Html.Attributes.classList [ x, y ]
Html.Attributes.classList [ ( onlyOneThing, True ) ]
--> Html.Attributes.class onlyOneThingJson.Decode.oneOf [ a ]
--> aParser.oneOf [ a ]
--> aRandom.uniform a []
--> Random.constant a
Random.weighted ( weight, a ) []
--> Random.constant a
Random.weighted tuple []
--> Random.constant (Tuple.first tuple)
Random.list 0 generator
--> Random.constant []
Random.list 1 generator
--> Random.map List.singleton generator
Random.list n (Random.constant el)
--> Random.constant (List.repeat n el)
Random.map identity generator
--> generator
Random.map (always a) generator
--> Random.constant a
Random.map f (Random.constant x)
--> Random.constant (f x)
Random.andThen f (Random.constant x)
--> f x
Random.andThen Random.constant generator
--> generator
Random.andThen (\a -> Random.constant b) generator
--> Random.map (\a -> b) generator
Random.andThen (always thenGenerator) generator
--> thenGenerator
Rule to simplify Elm code.