Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# - Why can’t I NOT use a pipeline operator in this code?

I’m probably missing something very fundamental here but I can’t figure out what it is.

I have some very basic code as below:

let myList = [1; 3; 5; 7; 5; 3; 9; 5; 2; 5; 3; 8]
let fiveOrLess = myList |> List.choose (fun number ->
    match number with
    | number when number <= 5 -> Some number
    | _ -> None)

… and that works just fine, as expected.

However, with the code below, where I have moved the “myList” from the pipe to just being a ‘normal’ parameter, I get two error messages from the compiler:

let myList = [1; 3; 5; 7; 5; 3; 9; 5; 2; 5; 3; 8]
let fiveOrLess = List.choose (fun number ->
    match number with
    | number when number <= 5 -> Some number
    | _ -> None) myList

Error FS3118 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. [This error on the second ‘let’.]

Error FS0010 Unexpected identifier in binding. Expected incomplete structured construct at or before this point or other token. [This error on the last “myList”.]

Nether of these error messages makes much sense to me as all I have done is move a parameter ‘from one side to the other’.

The documentation here: https://msdn.microsoft.com/visualfsharpdocs/conceptual/list.choose%5b%27t%2c%27u%5d-function-%5bfsharp%5d shows code which has a very similar form to my second sample but that works okay.

Can anyone explain what silly, basic mistake I’ve made here? Also, and possibly more importantly, can anyone explain what the error messages mean and why I’m getting them, both in general and in this context please?

like image 513
GarryP Avatar asked Dec 30 '22 22:12

GarryP


1 Answers

It is quite a subtle formatting issue but it seems that putting an argument on the same line immediately after a multi-line lambda, which starts on a let line, is not valid. Your code had non-standard formatting in a few different ways but the F# compiler was being fairly permissive until that last part where it the parser completely lost track of things and wasn't able to provide a helpful error.

So it's not that your formatting was clearly "wrong", but once you're in the habit of writing F# code following normal formatting standards you would never have that problem. Here are some standard ways to format the function:

let fiveOrLess =
    List.choose
        (fun number ->
            match number with
            | number when number <= 5 -> Some number
            | _ -> None)
        myList

The indentation above makes it clear what the two arguments to List.choose are.


let fiveOrLess =
    myList
    |> List.choose (fun number ->
        match number with
        | number when number <= 5 -> Some number
        | _ -> None)

We use piping above so that the multi-line lambda is the very last parameter.

like image 79
TheQuickBrownFox Avatar answered Jan 29 '23 07:01

TheQuickBrownFox