Javier Lorenzini
Javier Lorenzini

Reputation: 179

"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!

Upvotes: 2

Views: 7772

Answers (3)

Tomas Petricek
Tomas Petricek

Reputation: 243096

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.

Upvotes: 7

Søren Debois
Søren Debois

Reputation: 5688

Short and sweet:

let compare triple x = Array.exists ((=) triple) x

No need to unpack the tuple (i, j, x).

The notation (=) turns the equality comparison operator into a function of two curried arguments. When we partially apply (=) to triple, that is, when we write

(=) triple

we get the function which takes one argument, and compares that argument to triple, returning true or false.

Upvotes: 3

Chris Ballard
Chris Ballard

Reputation: 3769

The problem is the for loop expects the expression within it to evaluate to unit. You will need a different approach, for example using Array.exists

Upvotes: 2

Related Questions