Reports when an expression can be simplified.
🔧 Running with --fix
will automatically remove all the reported errors.
config =
[ Simplify.rule Simplify.defaults
]
rule : Configuration -> Review.Rule.Rule
Rule to simplify Elm code.
Configuration for this rule. Create a new one with defaults
and use ignoreCaseOfForTypes
and expectNaN
to alter it.
defaults : Configuration
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
]
expectNaN : Configuration -> Configuration
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
--> True
However, 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
--> False
Given the potential presence of NaN
, some simplifications become unsafe to apply:
x == x
to True
List.member x [ x ]
to True
n * 0
to 0
This 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
]
ignoreCaseOfForTypes : List String -> Configuration -> Configuration
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 Simplify
Below 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 >= b
x == True
--> x
x /= False
--> x
not x == not y
--> x == y
anything == anything
--> True
anything /= anything
--> False
{ r | a = 1 } == { r | a = 2 }
--> False
if 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 3
case 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
--> x
-- same with any variant, list or tuple containing either
case Just value of
Nothing -> a
Just (Ok b) -> c
Just (Err d) -> e
--> case value of
--> Ok b -> c
--> Err d -> e
Destructuring using case expressions
case value of
( x, y ) ->
x + y
-->
let
( x, y ) =
value
in
x + y
let
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.d
(Record first second).first
--> first
identity x
--> x
f >> identity
--> f
always x y
--> x
f >> always x
--> always x
toFloat 1
--> 1
round 1
--> 1
ceiling 1
--> 1
floor 1
--> 1
truncate 1
--> 1
round (toFloat n) -- same for ceiling, floor and truncate
--> n
(\_ -> x) data
--> x
(\() y -> x) ()
--> (\y -> x)
(\_ y -> x) data
--> (\y -> x)
(++) a b
--> a ++ b
a |> f >> g
--> a |> f |> g
n + 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
--> 0
Tuple.pair a b
--> ( a, b )
Tuple.first ( a, b )
--> a
Tuple.first (Tuple.mapSecond changeFirst tuple)
--> Tuple.first tuple
Tuple.first (Tuple.mapBoth changeFirst changeSecond tuple)
--> Tuple.first (Tuple.mapFirst changeFirst tuple)
Tuple.second ( a, b )
--> b
Tuple.second (Tuple.mapFirst changeSecond tuple)
--> Tuple.second tuple
Tuple.second (Tuple.mapBoth changeFirst changeSecond tuple)
--> Tuple.second (Tuple.mapSecond changeSecond tuple)
"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.concat [ string ]
--> string
String.concat [ hello, "", world ]
--> String.concat [ hello, world ]
String.concat [ "a", String.concat [ b, c ], d ]
--> String.concat [ "a", b, c, d ]
String.concat (List.repeat n str)
--> String.repeat n str
String.concat (List.intersperse str strings)
--> String.join str strings
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
--> ""
-- The following simplifications for String.foldl also work for String.foldr
String.foldl f initial ""
--> initial
String.foldl (\_ soFar -> soFar) initial string
--> initial
Maybe.map identity x
--> x
Maybe.map f Nothing
--> Nothing
Maybe.map f (Just x)
--> Just (f x)
-- the following simplifications for map2 work for all Maybe.mapN
Maybe.map2 f firstMaybe Nothing
--> Nothing
Maybe.map2 f (Just a) (Just b)
--> Just (f a b)
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)
--> y
Result.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.fromMaybe x (Just a)
--> Ok a
Result.fromMaybe x Nothing
--> Err x
Result.toMaybe (Ok x)
--> Just x
Result.toMaybe (Err e)
--> Nothing
a :: []
--> [ 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 ([a, b] ++ list)
--> 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.concat [ a, List.concat [ b, c ], d ]
--> List.concat [ a, b, c, d ]
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.sum [ a, 0, b ]
--> List.sum [ a, b ]
-- when `expectNaN` is enabled
List.sum [ a, 0 / 0, b ]
--> 0 / 0
List.product []
--> 1
List.product [ a ]
--> a
List.product [ a, 1, b ]
--> List.product [ a, b ]
-- when `expectNaN` is not enabled
List.product [ a, 0, b ]
--> 0
-- when `expectNaN` is enabled
List.product [ a, 0 / 0, b ]
--> 0 / 0
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.all identity [ a, False, b ]
--> False
List.all not [ a, True, b ]
--> False
List.all identity [ a, True, b ]
--> List.all identity [ a, b ]
List.all not [ a, False, b ]
--> List.all not [ a, b ]
List.any f []
--> True
List.any (always False) list
--> False
List.any identity [ a, True, b ]
--> True
List.any not [ a, False, b ]
--> True
List.any identity [ a, False, b ]
--> List.any identity [ a, b ]
List.any not [ a, True, b ]
--> List.any not [ a, b ]
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 )
Tuple.first (List.partition f list)
--> List.filter f list
List.take 0 list
--> []
List.drop 0 list
--> list
List.drop 3 [ a, b ]
--> []
List.drop 2 [ a, b, c ]
--> [ c ]
List.reverse []
--> []
List.reverse [ a ]
--> [ a ]
List.reverse (List.reverse list)
--> list
List.sort (List.sort list)
--> List.sort list
List.sortBy (always a) list
--> list
List.sortBy identity list
--> List.sort list
List.sortBy f (List.sortBy f list)
--> List.sortBy f 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.toList Array.empty
--> []
Array.toList (Array.repeat n a)
--> List.repeat n a
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.slice n n array
--> Array.empty
Array.slice n 0 array
--> Array.empty
Array.slice a z Array.empty
--> Array.empty
Array.slice 2 1 array
--> Array.empty
Array.slice -1 -2 array
--> Array.empty
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.get 2 (Array.repeat 10 x)
--> Just x
Array.get 100 (Array.repeat 10 x)
--> Nothing
Array.get 2 (Array.initialize 10 f)
--> Just (f 2)
Array.get 100 (Array.initialize 10 f)
--> 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 ]
-- The following simplifications for Array.foldl also work for Array.foldr
Array.foldl f initial Array.empty
--> initial
Array.foldl (\_ soFar -> soFar) initial array
--> initial
Array.toIndexedList Array.empty
--> []
List.map Tuple.second (Array.toIndexedList array)
--> Array.toList array
Array.length (Array.fromList list)
--> List.length list
-- The following simplification also works for Array.toIndexedList
List.length (Array.toList array)
--> Array.length array
-- The following simplification also works for Array.toIndexedList
List.isEmpty (Array.toList array)
--> Array.isEmpty array
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.isEmpty (Set.fromList ([a] ++ list)
--> False
Set.member x Set.empty
--> False
Set.toList Set.empty
--> []
Set.length Set.empty
--> 0
Set.intersect Set.empty set
--> Set.empty
Set.intersect set set
--> set
Set.diff Set.empty set
--> Set.empty
Set.diff set Set.empty
--> set
Set.union set Set.empty
--> set
Set.union set set
--> 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.filter (\_ -> True) set
--> set
Set.filter (\_ -> False) set
--> Set.empty
Set.partition f Set.empty
--> ( Set.empty, Set.empty )
Set.partition (always True) set
--> ( set, Set.empty )
Tuple.first (Set.partition f set)
--> Set.filter f set
-- The following simplifications for Set.foldl also work for Set.foldr
Set.foldl f initial Set.empty
--> initial
Set.foldl (\_ soFar -> soFar) initial set
--> initial
List.length (Set.toList set)
--> Set.size set
List.isEmpty (Set.toList set)
--> Set.isEmpty set
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.remove k Dict.empty
--> Dict.empty
Dict.filter f Dict.empty
--> Dict.empty
Dict.filter (\_ _ -> True) dict
--> dict
Dict.filter (\_ _ -> False) dict
--> Dict.empty
Dict.map f Dict.empty
--> Dict.empty
Dict.map (\_ value -> value) dict
--> dict
Dict.intersect Dict.empty dict
--> Dict.empty
Dict.intersect dict dict
--> dict
Dict.diff Dict.empty dict
--> Dict.empty
Dict.diff dict Dict.empty
--> dict
Dict.union dict Dict.empty
--> dict
Dict.union dict dict
--> 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 (\_ _ -> True) dict
--> ( dict, Dict.empty )
Dict.partition (\_ _ -> False) dict
--> ( Dict.empty, dict )
Tuple.first (Dict.partition f dict)
--> Dict.filter f dict
List.map Tuple.first (Dict.toList dict)
--> Dict.keys dict
List.map Tuple.second (Dict.toList dict)
--> Dict.values dict
-- same for foldr
Dict.foldl f initial Dict.empty
--> initial
Dict.foldl (\_ soFar -> soFar) initial dict
--> initial
-- The following simplification also works for Dict.keys, Dict.values
List.length (Dict.toList dict)
--> Dict.size dict
-- The following simplification also works for Dict.keys, Dict.values
List.isEmpty (Dict.toList dict)
--> Dict.isEmpty dict
All 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.batch [ a, Cmd.batch [ b, c ], d ]
--> Cmd.batch [ a, b, c, d ]
Cmd.map identity cmd
--> cmd
Cmd.map f Cmd.none
--> Cmd.none
Task.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) task
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 task
Html.Attributes.classList [ x, y, ( z, False ) ]
--> Html.Attributes.classList [ x, y ]
Html.Attributes.classList [ ( onlyOneThing, True ) ]
--> Html.Attributes.class onlyOneThing
Json.Decode.map identity decoder
--> decoder
Json.Decode.map f (Json.Decode.fail x)
--> Json.Decode.fail x
Json.Decode.map f (Json.Decode.succeed a)
--> Json.Decode.succeed (f a)
-- the following simplifications for map3 work for all Json.Decode.mapN
Json.Decode.map3 f (Json.Decode.succeed a) (Json.Decode.succeed b) (Json.Decode.succeed c)
--> Json.Decode.succeed (f a b c)
Json.Decode.map3 f (Json.Decode.succeed a) (Json.Decode.fail x) thirdDecoder
--> Json.Decode.fail x
Json.Decode.map3 f firstDecoder (Json.Decode.fail x) thirdDecoder
--> Json.Decode.map2 f firstDecoder (Json.Decode.fail x)
Json.Decode.andThen f (Json.Decode.fail x)
--> Json.Decode.fail x
Json.Decode.andThen f (Json.Decode.succeed a)
--> f a
Json.Decode.andThen Json.Decode.succeed decoder
--> decoder
Json.Decode.andThen (\a -> Json.Decode.succeed b) decoder
--> Json.Decode.map (\a -> b) decoder
Json.Decode.oneOf [ a ]
--> a
Parser.oneOf [ a ]
--> a
Random.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
Test.concat [ test ]
--> test
Test.concat [ test0, Test.concat [ test1, test2 ], test3 ]
--> Test.concat [ test0, test1, test2, test3 ]