This module contains various Predicates that can be used to filter
pipelines.
Predicates can be combined and negated using and, or, and doNot.
Create a Predicate that matches pipelines that match either or both of two
predicates.
Negate a Predicate.
Checks whether or not a pipeline spans multiple lines of code.
Checks whether the length of a pipeline is longer than a specified number. Note that the length of a pipeline is the number of operators in it, e.g.
foo
|> bar
|> bazhas length 2 for the purposes of this predicate.
Checks whether the length of a pipeline is less than a specified number. Note that the length of a pipeline is the number of operators in it, e.g.
foo
|> bar
|> bazhas length 2 for the purposes of this predicate.
Determine whether the pipeline has a simple input or not. This is somewhat
subjective, of course. A pipeline is considered to have a simple input if it
has an unnecessary input, if its second step is
not a semantically-infix function (like logBase
), and if its input step is simple.
This predicate is constructed as follows:
haveASimpleInputStep =
haveAnUnnecessaryInputStep
|> and (doNot <| haveASecondStepThatIs aSemanticallyInfixFunction)
|> and (haveAnInputStepThatIs aSimpleStep)Determine whether the pipeline has an input step that simply isn't
necessary, e.g. foo |> bar |> baz, which may be written as bar foo |> baz.
This is not perfectly exhaustive as it does not consider operator precedence and the like but will suffice for finding most simple cases.
Note that this will potentially flag quite complex inputs, so you might want to
use haveASimpleInputStep instead, since that only
detects visually simple inputs.
This predicate is constructed as follows:
haveAnUnnecessaryInputStep =
let
ableToBeAnArgument =
stepPredicate <|
\node ->
case Node.value node of
Application _ ->
False
OperatorApplication _ _ _ _ ->
False
IfBlock _ _ _ ->
False
LetExpression _ ->
False
CaseExpression _ ->
False
LambdaExpression _ ->
False
GLSLExpression _ ->
False
_ ->
True
ableToTakeAnArgument =
stepPredicate <|
\node ->
case Node.value node of
Application _ ->
True
FunctionOrValue _ _ ->
True
PrefixOperator _ ->
True
RecordAccessFunction _ ->
True
ParenthesizedExpression _ ->
True
RecordAccess _ _ ->
True
_ ->
False
in
haveAnInputStepThatIs ableToBeAnArgument
|> and (haveASecondStepThatIs ableToTakeAnArgument)Checks if an operator (typically left pizza (<|)) is used in the
"canonical" fashion in a test suite, to separate the lambda containing the test
from the test. All of the following will pass this predicate, and all other
uses will not:
import Test exposing (..)
suite =
describe "tests"
[ test "foo" <|
\() ->
a
, fuzz fooFuzz "fuzz" <|
\foo ->
a
, fuzz2 fooFuzz barFuzz "fuzz2" <|
\foo bar ->
a
, fuzz3 fooFuzz barFuzz bazFuzz "fuzz3" <|
\foo bar baz ->
a
, fuzzWith { runs = 117 } fooFuzz "fuzzWith" <|
\foo ->
a
]Here is how to construct this predicate, for an example of how to build complex predicates:
separateATestFromItsLambda =
let
aLambdaExpression : StepPredicate
aLambdaExpression =
stepPredicate <|
\node ->
case Node.value node of
LambdaExpression _ ->
True
_ ->
False
aTestFunction : StepPredicate
aTestFunction =
stepPredicateWithLookupTable <|
\lookupTable node ->
case Node.value node of
Application (h :: _) ->
case ( Node.value h, moduleNameFor lookupTable h ) of
( FunctionOrValue _ n, Just [ "Test" ] ) ->
List.member n
[ "test"
, "fuzz"
, "fuzz2"
, "fuzz3"
, "fuzzWith"
]
_ ->
False
_ ->
False
in
haveFewerStepsThan 3
|> and (haveAnInputStepThatIs aLambdaExpression)
|> and (haveASecondStepThatIs aTestFunction)Checks whether any comments are located within the pipeline, e.g.
a =
foo
-- Comment
|> bar
|> bazComments around the pipeline are ignored, e.g.
a =
-- Ignored
foo
|> bar
|> baz
-- IgnoredThese predicates allow one to filter based on a specific step of a pipeline.
Given a predicate for a single step, check if the first step in a pipeline matches said predicate.
Given a predicate for a single step, check if the second step in a pipeline matches said predicate.
Given a predicate for a single step, check if any step in a pipeline except for the first matches said predicate.
Given a predicate for a single step, check if all steps in a pipeline except for the first match said predicate.
Given a predicate for a single step, check if any step in a pipeline matches said predicate.
Given a predicate for a single step, check if all step in a pipeline match said predicate.
A "semantically-infix" function is a function that is intended (by name) to
be read in an infix fashion as part of a pipeline. For example, Maybe.andThen
or remainderBy or Maybe.Extra.orElse:
List.head |> Maybe.andThen String.toFloat
10 |> remainderBy 2In practice, it checks if a step begins with a function that begins with the word "or" or "and" or ends in "By", or is on the following whitelist of functions:
logBaseatMostatLeastIt also rules out confusing, non-commutative functions, even if they match the above.
If you have suggestions for additions to this list, please open an issue or PR on Github: https://github.com/SiriusStarr/elm-review-pipeline-styles/issues
This checks if a step is a commonly-confused, non-commutative function by checking if it is on the following blacklist of such functions:
Basics.(-)
Basics.(/)
Basics.(//)
Basics.(^)
Basics.(<)
Basics.(>)
Basics.(<=)
Basics.(>=)
Basics.(++)
Basics.(>>)
Basics.(<<)
Basics.compare
Parser.(|.)
Parser.(|=)
Parser.Advanced.(|.)
Parser.Advanced.(|=)
Url.Parser.(</>)
Url.Parser.(<?>)
Array.append
Dict.diff
List.append
Set.diff
String.append
Basics.Extra.safeDivide
Basics.Extra.safeIntegerDivide
IntDict.diff
Maybe.Extra.or
Result.Extra.or
If you have suggestions for additions to this list, please open an issue or PR on Github: https://github.com/SiriusStarr/elm-review-pipeline-styles/issues
Checks if a step is "visually" simple. This is of course extremely
subjective; if you require different behavior, you can use
stepPredicate to fully customize it. A step is considered
"simple" if it is 40 characters or less, is only a single line, and is
one of the following:
-- Unit
()
-- Name
a
-- Prefix operator
(+)
-- Int literal
1
-- Hex literal
0x0F
-- Float literal
1.5
-- String literal
"bar"
-- Char literal
'c'
-- Record access function
.fieldor is one of the following where all subexpressions are simple:
-- Application
foo bar baz
-- Tuple
( a, "b" )
-- Record
{ a = "value" }
-- List
[]
-- Record access
a.field
-- Negation
elmFormatWontLetThisBeAnExample
-- Parentheses
elmFormatWontLetThisBeAnExampleChecks if a step consists of a single line of code.
Checks if a step consists of more than one line of code.
Given a function of type Node Expression -> Bool, create a StepPredicate
from it. This is only useful if you want to write custom predicates for steps.
If you think a generally useful step predicate is missing, please open an issue or PR on Github: https://github.com/SiriusStarr/elm-review-pipeline-styles/issues
Given a function of type ModuleNameLookupTable -> Node Expression -> Bool,
create a StepPredicate from it. This is only useful if you want to write
custom predicates for steps and need full module names.
If you think a generally useful step predicate is missing, please open an issue or PR on Github: https://github.com/SiriusStarr/elm-review-pipeline-styles/issues
Checks whether the pipeline is nested to any degree within another pipeline. Note that this is quite a strict requirement and you probably want to use one of the other nesting predicates instead.
Checks whether the immediate parent of a pipeline (if one exists) is not separated by one of a list of acceptable abstractions.
Checks whether the pipeline is nested to a greater degree than specified
within other pipelines. For example, haveMoreNestedParentsThan 1 will forbid
a =
foo
|> (bar <| (a |> b |> c))
|> bazEither within a let declaration or in the let expression of a let
block from the surrounding pipeline.
Within a lambda function in the surrounding pipeline.
Within a case expression of if...then expression in the surrounding
pipeline.
Within a tuple, list, or record in the surrounding pipeline.
If you need predicates beyond what is provided above, you can create them
manually by writing a function of type Pipeline -> Bool or
ModuleNameLookupTable -> Pipeline -> Bool and using one of the functions
below.
Use the functions in Getting Information About Pipelines to build your custom predicate.
Given a function of type Pipeline -> Bool, create a Predicate from it.
This is only useful if you want to write custom predicates. Note that this will
allow you to create predicates that match any type of pipeline, so be careful in
how you use it.
If you think a generally useful predicate is missing, please open an issue or PR on Github: https://github.com/SiriusStarr/elm-review-pipeline-styles/issues
Given a function of type ModuleNameLookupTable -> Pipeline -> Bool, create
a Predicate from it. This is only useful if you want to write custom
predicates and are going to need the full module name for expressions in the
pipeline. Note that this will allow you to create predicates that match any type
of pipeline, so be careful in how you use it.
If you think a generally useful predicate is missing, please open an issue or PR on Github: https://github.com/SiriusStarr/elm-review-pipeline-styles/issues
Note that some of the types returned by these functions are from
stil4m/elm-syntax
if you need to work with them directly.
Get the "steps" in the pipeline. Note that this is in "logical" order, e.g.
a 1 >> b >> c, c << b << a 1, a 1 |> b |> c, c <| b <| a 1, and
c (b (a 1)) will all have the same steps of [a 1, b, c].
Get the "parent" pipelines of the pipeline, i.e. a hierarchy of pipelines
that this pipeline is nested within (start of the list being the most immediate
parent). Note that you can use the functions in
Nesting Predicates to interrogate the NestedWithin
type and in Query Pipeline Types for the
Operator type.
Get the outermost Node of a pipeline; you probably don't need to work with
this directly.
Get a list of all comments that are inside of a pipeline.
Check if an Operator is the right "pizza" operator (right function
application), i.e. |>. An example of this pipeline is below:
foo
|> bar
|> bazCheck if an Operator is the left "pizza" operator (left function
application), i.e. <|. An example of this pipeline is below:
foo <| bar <| bazCheck if an Operator is the right composition operator, i.e. >>. An
example of this pipeline is below:
foo
>> bar
>> bazCheck if an Operator is the left composition operator, i.e. <<. An
example of this pipeline is below:
foo << bar << bazCheck if an Operator is simply parenthetical application (not an
operator), i.e. successive function calls using parentheses, e.g.
foo (bar (baz (i (j k))))These are exposed only for the sake of type annotations; you shouldn't need to work with them directly.
A predicate for filtering pipelines, or a logical combination of them.
A predicate for a single step of a pipeline.
The operator type of a pipeline, e.g. |> or <<.
A detected pipeline. You only need be concerned with this type if you are writing a manual predicate.
The degree to which a parent or child is removed from a pipeline.
Pipelines that are function application.
Pipelines that are function composition.
These functions have been deprecated and are included only to avoid a breaking change.
DEPRECATED: Use haveAnInputStepThatIs
instead.
Create a
Predicatethat matches pipelines that match both of two predicates.