Question

I'm trying to learn F# by going through some of the Euler problems and I found an issue I haven't been able to figure out. This is my naive solution.

let compute =
    let mutable f = false
    let mutable nr = 0
    while f = false do
        nr <- nr + 20
        f = checkMod nr
    nr

When i do this I get the error message warning FS0020: This expression should have type 'unit', but has type 'bool' on the expression "nr <- nr +20". I've tried rewriting and moving the expressions around and I always get that error on the line below the while statement.

I'm writing this using VS2010 Beta.

Was it helpful?

Solution

The following line:

f = checkMod nr

is an equality check, not an assignment as I believe you are intending. Change it to:

f <- checkMod nr

and all should work fine. I'm not sure why you've used the correct syntax on the previous line and not that line...

Also, the line while f = false do should really be simplified to while not f do; equality checks on booleans are rather convoluted.

As I side note, I feel a need to point out that you are effectively trying to use F# as an imperative language. Use of mutable variables and while loops are strongly discouraged in functional languages (including F#), especially when a purely functional (and simpler) solution exists, as in this situation. I recommend you read up a bit on programming in the functional style. Of course, just getting to grips with the syntax is a useful thing in itself.

OTHER TIPS

Since I can imagine this weg page becoming the 'canonical' place to look up information about warning FS0020, here's my quick summary of the three commonest cases in which you get the warning, and how to fix them.

Intentionally discarding the result of a function that is called only for its side-effects:

// you are calling a function for its side-effects, intend to ignore result    
let Example1Orig() =
    let sb = new System.Text.StringBuilder()
    sb.Append("hi")       // warning FS0020
    sb.Append(" there")   // warning FS0020
    sb.ToString()

let Example1Fixed() =
    let sb = new System.Text.StringBuilder()
    sb.Append("hi") |> ignore
    sb.Append(" there") |> ignore
    sb.ToString()

Warning is useful, pointing out an error (function has no effects):

// the warning is telling you useful info 
// (e.g. function does not have an effect, rather returns a value)
let Example2Orig() =
    let l = [1;2;3] 
    List.map (fun x -> x * 2) l    // warning FS0020
    printfn "doubled list is %A" l

let Example2Fixed() =
    let l = [1;2;3] 
    let result = List.map (fun x -> x * 2) l
    printfn "doubled list is %A" result

Confusing assignment operator and equality comparison operator:

// '=' versus '<-'
let Example3Orig() =
    let mutable x = 3
    x = x + 1          // warning FS0020
    printfn "%d" x    

let Example3Fixed() =
    let mutable x = 3
    x <- x + 1
    printfn "%d" x    

If you're trying to adopt the functional style, try to avoid mutable values.

For example like this:

let nr =
   let rec compute nr =  
      if checkMod nr then nr else compute (nr + 20)
   compute 0     

while expressions in F# take a little getting used to if you're coming from an imperative language. Each line in a while expression must evaluate to unit (think void from C++/C#). The overall expression then also evaluates to unit.

In the example:

nr <- nr + 20

evaluates to unit whereas

f = checkMod nr

evaluates to a bool as Noldorin noted. This results in a warning message being reported. You can actually turn the warning off if you so desire. Just put the following at the top of your file:

#nowarn "0020"

I've been programming in an imperative style for a long time, so getting used to the functional programming mindset took a while.

In your example, you're trying to find the first multiple of 20 that passes your checkMod test. That's the what part. For the functional how part, I recommend browsing through the methods available to sequences. What you need is the first element of a sequence (multiples of 20) passing your test, like this:

let multi20 = Seq.initInfinite (fun i -> i*20)
let compute = multi20 |> Seq.find checkMod

The first let generates an infinite list of twentyples (I made that one up). The second let finds the first number in said list that passes your test. Your task is to make sure that there actually is a number that will pass the test, but that's of course also true for the imperative code.

If you want to condense the two above lines into one, you can also write

let computeCryptic = Seq.initInfinite ((*) 20) |> Seq.find checkMod

but I find that pulling stunts like that in code can lead to headaches when trying to read it a few weeks later.

In the same spirit as Brian's post, here is another way to get warning FS0020: In a nutshell, I accidentally tupled the function arguments.

Being an F# newbie, I had a difficult time debugging the code below, which for the second line (let gdp...) gave the warning FS0020: This expression should have type 'unit', but has type '(string -> ^a -> unit) * string * float'. It turns out that line was not the problem at all; instead, it was the printfn line that was messed up. Removing the comma separators from the argument list fixed it.

for country in wb.Regions.``Arab World``.Countries do
  let gdp = country.Indicators.``GDP per capita (current US$)``.[2010]
  let gdpThous = gdp / 1.0e3
  printfn "%s, %s (%.2f)" country.Name, country.CapitalCity, gdpThous
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top