Search code examples
typesf#tuplesunit-type

"This expression was expected to have type unit" but I want it to be boolean


I'm trying to make a function that compares a tuple with the values of an array of tuples. I need this to return a boolean, but VS2013 keeps telling me: "This expression was expected to have type 'unit', but has type 'bool'"

let compare (i,j,a,(x : (int*int*int) array)) =
    for q in 0 .. x.GetLength(0) do  
        match (i,j,a,x) with
        | (i,j,a,x) when (i,j,a) = x.[q] -> true
        |  _ -> false

Also tried to give the values as two parameters, but it doesn't work either:

let compare (i,j,a) (x : (int*int*int) array) =
    for q in 0 .. x.GetLength(0) do  
        match (i,j,a) with
        | x.[q] -> true
        |  _ -> false

Any help would be appreciated!


Solution

  • To expand a little bit on the answer from Chris, the main reason why your code does not work is that F# is an expression-based language and does not have imperative-style control-flow that you might be expecting here (if you incorrectly read the body of the expression as return keyword).

    As F# is expression-based, everything is an expression that evaluates to a value. So, true is an expression that evaluates to true (boolean). Pattern matching from your example:

    match (i,j,a,x) with
    | (i,j,a,x) when (i,j,a) = x.[q] -> true
    |  _ -> false
    

    ... is also an expression that evaluates to true or false (boolean value) depending on the values of variables. In this case, conditional would be simpler though:

    if (i,j,a,x) = x.[q] then true else false
    

    ... or you could just write the condition (i,j,a,x) = x.[q] which means exactly the same thing. Now, for loop is tricky, because it evaluates the body multiple times and so it would potentially get multiple return values. For this reason, F# has a special type unit (think void in C#) which represents a value that does not carry any information. So, for loop expects unit-returning body like:

    for i in 1 .. 10 do 
      printfn "Foo"
    

    You can check that printfn "Foo" actually returns unit value by writing for example:

    for i in 1 .. 10 do 
      let nothing = printfn "Foo"
      nothing
    

    If you place mouse pointer over nothing, you'll see that it is of type unit.

    So, for loop is not a good approach if you want to break the iteration in the middle (because it cannot be done in F#). As already mentioned by Chris and Søren you can use functions from the Array module to easily implement the logic. Alternatively, you would have to write this using recursion or mutable variables - but the Array module works nicely here.