ctw28
ctw28

Reputation: 21

This expression was expected to have type bool but here has type unit error

getting an error when I try to run this line of code and I can't figure out why

    let validCol column value : bool = 
        for i in 0..8 do
            if sudokuBoard.[i,column] = value then 
                false
            else true

Upvotes: 2

Views: 576

Answers (3)

It looks like you are looking for a short-cut out of the loop like in C# you can use continue, break or return to exit a loop.

In F# the way to accomplish that with performance is to use tail-recursion. You could achieve it with while loops but that requires mutable variables which tail-recursion doesn't need (although we sometimes uses it).

A tail-recursive function is one that calls itself at the very end and doesn't look at the result:

So this is tail-recursive

let rec loop acc i = if i > 0 then loop (acc + i) (i - 1) else acc

Where this isn't

let rec loop fib i = if i < 1 then 1 else fib (i - 1) + fib (i - 2)

If F# compiler determines a function is tail-recursive the compiler applies tail-recursion optimization (TCO) on the function, basically it unrolls it into an efficient for loop that looks a lot like the loop would like in C#.

So here is one way to write validCol using tail-recursion:

let validCol column value : bool =
  // loops is tail-recursive
  let rec loop column value i =
    if i < 9 then
      if sudokuBoard.[i,column] = value then 
        false // The value already exists in the column, not valid
      else
        loop column value (i + 1)  // Check next row.
    else
      true  // Reach the end, the value is valid
  loop column value 0

Unfortunately; F# compiler doesn't have an attribute to force TCO (like Scala or kotlin does) and therefore if you make a slight mistake you might end up with a function that isn't TCO. I think I saw GitHub issue about adding such an attribute.

PS. seq comprehensions are nice in many cases but for a sudoku solver I assume you are looking for something that is as fast as possible. seq comprehensions (and LINQ) I think adds too much overhead for a sudoku solver whereas tail-recursion is about as quick as you can get in F#.

PS. In .NET 2D arrays are slower than 1D arrays, just FYI. Unsure if it has improved with dotnet core.

Upvotes: 1

AMieres
AMieres

Reputation: 5014

As Tyler Hartwig says a for loop cannot return a value except unit.

On the other hand, inside a list comprehension or a seq Computation Expression you can use for to yield the values and then test if the one you are looking for exists:

let validCol column value : bool = 
    seq { for i in 0..8 do yield sudokuBoard.[i,column] }
    |> Seq.exists value
    |> not

Upvotes: 2

Tyler Hartwig
Tyler Hartwig

Reputation: 660

In F#, the last call made is what is returned, you have explicitly declared you are returning a bool.

The for loop is unable to return or aggregate multiple values, bun instead, returns unit.

let validCol column value : bool = 
    for i in 0..8 do
        if sudokuBoard.[i,column] = value then 
            false
        else 
            true

Here, you'll need to figure out how to aggregate all the bool to get your final result. I'm not quite sure what this is supposed to return, or I'd give an example.

Upvotes: 1

Related Questions