Skip to content

Improve error reporting: Warn when pipe operators are incorrectly indented #1442

@rmunn

Description

@rmunn

What

Another one for the #1103 list. A recent F# question on StackOverflow highlighted a place where the compiler's error messages are not helpful. The user wrote the following code:

let process_args argv =
    let (result, _) = ((default_settings, "-f"), argv)
        ||> Array.fold (fun (state, setting) elem ->
            // (snip body of folder function as it's irrelevant here)
        )
    result

Note that the user's original code used the verbose-syntax usage of let ... in here, which I've replaced with light-syntax let (without in) since the syntax makes no difference in this case and the same unhelpful compiler errors appear.

This function produces two error messages: one on the let of let (result, _) = ..., and one on the ||> operator. The error on let is:

Incomplete value or function definition. If this is in an expression, the body of the expression must be indented to the same column as the 'let' keyword.

and the error on the ||> is:

Unexpected infix operator in binding. Expected incomplete structured construct at or before this point or other token.

Why

The StackOverflow user asking this question appeared to understand F# pretty well, but he was not able to figure out from those two error messages that he had an indentation problem with his pipe operator. It should have been indented far enough to be under the ((default_settings, "-f"), argv) tuple, but the error messages on let and ||> give no clue to that fact.

How

The code pattern of let something = somethingElse |> someFunc is extremely common. If the |> operator (or any other pipe operator like ||>, <|, and so on) is found "alone" on a line when the previous line had a let statement, it's likely that the user meant to indent the |> to line up with somethingElse. So a nicer error message for the original user's code might have been:

Unexpected infix operator in binding.
The ||> operator should be indented so that it is aligned under the value it operates on.
Instead of this:

    let (result, _) = ((default_settings, "-f"), argv)
        ||> Array.fold (fun (state, setting) elem ->

you probably meant to write this:

    let (result, _) =
        ((default_settings, "-f"), argv)
        ||> Array.fold (fun (state, setting) elem ->

or possibly this:

    let (result, _) = ((default_settings, "-f"), argv)
                      ||> Array.fold (fun (state, setting) elem ->

With, of course, the actual operator in the sentence "The ||> operator should be indented ...". This will usually be the |> operator, since ||> has very few good use cases outside of piping into a .fold -- but the same error message should be applicable to all the piping operators.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Feature ImprovementTheme-Simple-F#A cross-community initiative called "Simple F#", keeping people in the sweet spot of the language.

    Type

    No type

    Projects

    Status

    In Progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions