Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Indentation problem in f# (vs2010 beta1)

I'm just learning f# so possibly I'm doing something very silly. Feel free to point me to the relevant documentation, I've searched but wasn't able to find. I am using Visual Studio 2010 beta on windows 7 (.Net 4.0).

Everything is going well with my first f# project. Well.. almost everything. In particular I'm writing a very simple linear interpolation function, with the following code:

let linterp (x:double) (xvalues:double list) (yvalues:double list) =
    let num_els = xvalues.Length
    if x <= xvalues.Head then 
        let result = yvalues.Head
    elif x >= (List.rev xvalues).Head then 
        let result = (List.rev yvalues).Head
    else for idx in [0 .. num_els] do 
        if List.nth xvalues idx >= x then 
            let x0 = xvalues.Item idx
            let y0 = yvalues.Item idx
            let x1 = xvalues.Item (idx+1)
            let y1 = yvalues.Item (idx+1)
            let result = y0 + (y1-y0)/(x1-x0)*(x - x0)
    result

and I am receiving a series of errors which completely elude my comprehension.

This is the list of errors and warnings:

  • "error in the return expression for this 'let'. Possible correct indentation" for the first, second and last "let".

  • "possible incorrect indentation: this token is offside of context started at position (39:10). Try indenting this token further or using standard formatting conventions " for the "if"

  • "incomplete structured construct at or before this point in expression" for the last line (result).

I will add that I had to sweat a bit to annotate the types, since for some reason unknown to me the compiler was able to infer correctly for the first list, but for the second the type was always inferred as unit Also, my original version didn't bind the name result, but simply "returned the expression", like in

if x <= xvalues.Head then 
    yvalues.Head

or in

else for idx in [0 .. num_els] do 
    if List.nth xvalues idx >= x then 
        let x0 = xvalues.Item idx
        let y0 = yvalues.Item idx
        let x1 = xvalues.Item (idx+1)
        let y1 = yvalues.Item (idx+1)
        y0 + (y1-y0)/(x1-x0)*(x - x0)

This leaves an error under the "for", saying that "This expression has type unit but is here used with type double" and that the "if" is possibly incorrectly indented.

I think that when I'll see the solution to this I'll feel silly, but I've been stuck on such a simple problem for more than an hour, so I am asking for your help.

Thanks in advance!

ps: I've checked that the tabs are correctly interpreted as spaces in the Tools->options->.... -> F#->Tabs menu

pps: this is my first question on SO :-)

like image 777
Francesco Avatar asked Dec 30 '22 17:12

Francesco


1 Answers

Your problem is that something like

let result = yvalues.Head

is not a complete expression, and so it can't form the body of one of the branches of an if block. Your initial approach was correct, except that a for ... do loop doesn't return a meaningful value (it returns (), which is the only value of type unit, as the compiler is trying to explain; this is similar to void in languages like C#). Instead of the for loop, you'll want to use an expression that has the value you're looking for. The smallest change to your code would be to use a mutable value which you imperatively set in the loop. A more idiomatic approach would be to use a built in function like List.fold to condense the list to a single value. This is complicated by the fact that you need to access consecutive entries in your lists (and that you need to operate on both xvalues and yvalues simulataneously), which means you'd probably need to use List.zip and Seq.pairwise, which might reduce clarity for someone not used to F#.

Additionally, there are a few other changes you can apply to make your code more idiomatic. For instance, let x0 = xvalues.Item idx would more commonly be written let x0 = xvalues.[idx]. However, note that F# lists are immutable, linked lists, and therefore do not support fast random access. This is another reason to favor an approach that uses the built-in List operators.

like image 125
kvb Avatar answered Jan 09 '23 16:01

kvb