This package module some commonly useful rules, as well as how to construct
them, both so that one might use them as is but also get a sense of how to
construct one's own PipelineRules.
These PipelineRules forbid left composition (<<) pipelines that span
multiple lines. Multiple operator pipelines will be converted to right
composition (>>) pipelines, while single operator ones will (try) to be fixed
by placing them on a single line.
For example:
foo
<< bar
<< baz
a
<< bwill be converted to
baz
>> bar
>> foo
a << bConfiguration:
noMultilineLeftComposition =
[ forbid leftCompositionPipelines
|> that
(spanMultipleLines
|> and (haveMoreStepsThan 1)
)
|> andTryToFixThemBy convertingToRightComposition
|> andCallThem "multiline << pipeline with several steps"
, forbid leftCompositionPipelines
|> that
(spanMultipleLines
|> and (haveFewerStepsThan 2)
)
|> andTryToFixThemBy makingSingleLine
|> andCallThem "multiline << pipeline with one step"
]These PipelineRules forbid "right pizza" (|>) pipelines that are
entirely on a single line and try to fix them by making them multiline.
For example:
foo |> bar |> bazwill be converted to:
foo
|> bar
|> bazConfiguration:
noSingleLineRightPizza =
[ forbid rightPizzaPipelines
|> that (doNot spanMultipleLines)
|> andTryToFixThemBy makingMultiline
|> andCallThem "single line |> pipeline"
]These PipelineRules forbid right composition (>>) pipelines that are
entirely on a single line and try to fix them by making them multiline.
For example:
foo >> bar >> bazwill be converted to:
foo
>> bar
>> bazConfiguration:
noSingleLineRightComposition =
[ forbid rightCompositionPipelines
|> that (doNot spanMultipleLines)
|> andTryToFixThemBy makingMultiline
|> andCallThem "single line >> pipeline"
]These PipelineRules forbid "right pizza" (|>) and "left pizza" (<|)
pipelines that have "simple" (unnecessary) inputs and try to fix them by
eliminating the input step.
For example:
foo |> bar |> baz
foo <| bar <| bazwill be converted to:
bar foo |> baz
foo <| bar bazNote that all unnecessary left pipeline inputs will be removed (since those operators do not even add clarity), whereas only visually-simple right pipeline inputs are removed
Configuration:
noPipelinesWithSimpleInputs =
[ forbid rightPizzaPipelines
|> that haveASimpleInputStep
|> andTryToFixThemBy eliminatingInputStep
|> andCallThem "|> pipeline with simple input"
, forbid leftPizzaPipelines
|> that haveAnUnnecessaryInputStep
|> andTryToFixThemBy eliminatingInputStep
|> andCallThem "<| pipeline with simple input"
]These PipelineRules forbid parenthetical application with more than a
single step and try to fix it by converting it to "right pizza" (|>) pipeline.
For example:
foo (bar (baz i))will be converted to:
baz i
|> bar
|> fooIt excludes ones that are nested immediately within a pipeline already, as is
often the case with e.g. nested maps. For example:
foo
|> Maybe.map (Result.map (yi << er))
|> barwill not be flagged, as it cannot be converted to a "right pizza" (|>)
pipeline or written in a way that is particularly "nicer."
Configuration:
noRepeatedParentheticalApplication =
forbid parentheticalApplicationPipelines
|> that
(haveMoreStepsThan 1
|> and
(doNot
(haveAParentNotSeparatedBy
[ aLetBlock
, aLambdaFunction
, aFlowControlStructure
, aDataStructure
]
)
)
)
|> andTryToFixThemBy convertingToRightPizza
|> andCallThem "parenthetical application with several steps"These PipelineRules forbid any pipeline that uses a non-commutative
function with commonly confused argument order. It cannot provide fixes.
For example, the following are flagged by this rule:
startOfList |> (++) endOfList |> whoops
keepDict |> Dict.diff subtractDict |> whoops
1 |> (-) 2 |> whoops
startOfList |> List.append endOfList |> whoopsThe following however are not flagged:
1 |> (+) 2 |> commutativeFunction
dict1 |> Dict.union |> dict2 |> commutativeFunction
foo |> bar |> bazNote that left pipelines and parenthetical application pipelines are only
flagged with confusing prefix operators, not functions like compare, since the
arguments are in the correct order in those cases.
Configuration:
noPipelinesWithConfusingNonCommutativeFunctions =
[ forbid rightPizzaPipelines
|> that (haveAnyStepThatIs aConfusingNonCommutativeFunction)
|> andCallThem "|> pipeline with confusing non-commutative function"
, forbid rightCompositionPipelines
|> that (haveAnyStepThatIs aConfusingNonCommutativeFunction)
|> andCallThem ">> pipeline with confusing non-commutative function"
, forbid leftPizzaPipelines
|> that (haveAnyStepThatIs aConfusingNonCommutativePrefixOperator)
|> andCallThem "<| pipeline with confusing non-commutative prefix operator"
, forbid leftCompositionPipelines
|> that (haveAnyStepThatIs aConfusingNonCommutativePrefixOperator)
|> andCallThem "<< pipeline with confusing non-commutative prefix operator"
, forbid parentheticalApplicationPipelines
|> that (haveAnyStepThatIs aConfusingNonCommutativePrefixOperator)
|> andCallThem "parenthetical application pipeline with confusing non-commutative prefix operator"
]These PipelineRules forbid any left pipelines that use a function named in
a "semantically-infix" fashion, i.e. whose function name is intended to be read
with an argument on each side. It fixes them by converting them to the
equivalent right pipeline.
For example:
Maybe.andThen String.toFloat << List.head
remainderBy 2 <| 1 + 3
logBase 2 (10 + 2)will be converted to:
List.head >> Maybe.andThen String.toFloat
1 + 3 |> remainderBy 2
10 + 2 |> logBase 2Configuration:
noSemanticallyInfixFunctionsInLeftPipelines =
[ forbid leftPizzaPipelines
|> that (haveAnyNonInputStepThatIs aSemanticallyInfixFunction)
|> andTryToFixThemBy convertingToRightPizza
|> andCallThem "<| pipeline with a semantically-infix function"
, forbid leftCompositionPipelines
|> that (haveAnyNonInputStepThatIs aSemanticallyInfixFunction)
|> andTryToFixThemBy convertingToRightComposition
|> andCallThem "<< pipeline with a semantically-infix function"
, forbid parentheticalApplicationPipelines
|> that (haveAnyNonInputStepThatIs aSemanticallyInfixFunction)
|> andTryToFixThemBy convertingToRightPizza
|> andCallThem "parenthetical application pipeline with a semantically-infix function"
]
These
PipelineRules forbid "left pizza" (<|) pipelines that span multiple lines, except for those that are used in the common/"canonical" case in tests, separating a test from its lambda function. Multiple operator pipelines will be converted to "right pizza" (|>) pipelines, while single operator ones will (try) to be fixed by placing them on a single line.For example:
will be converted to
Configuration: