Dread
Dread

Reputation: 87

F# If statement expecting unit from float

the statement is fine until the final elif which it then has the error "Was expected to have type unit"

type BankAcc = {AccNum:int; mutable  Balance:float} with

    member this.Withdraw(amount,?withdrawal) =
     let withdrawAmount = this.Balance * float amount
     match withdrawal with
      | None -> withdrawAmount
      | Some deduct -> withdrawAmount - deduct
let Account ={AccNum=123;Balance = 15.00}

Account.Withdraw(25.00) // withdrawing 25 from an account with a balance of 15

let test Balance withdrawAmount =
  if Balance = withdrawAmount then "Equals"
  elif Balance < withdrawAmount then "Balance too low"
  else Balance - withdrawAmount 

  Account={AccNum =0001; Balance=0.00};   

let CheckAccount Balance = 
    if Balance < 10.00 then "Balance is low"
    elif Balance >= 10.00 && Balance <= 100.00 then "Balance is ok"
    elif Balance > 100.00 then "balance is high"

let sort = Seq.unfold(fun Balance -> if(snd Balance => 50)then List.map(fun accounts-> Balance <50) list1)

Upvotes: 2

Views: 115

Answers (1)

Fyodor Soikin
Fyodor Soikin

Reputation: 80744

So let's abstract your code a little bit:

if a then b
elif x then y
elif p then q

From this, the compiler can tell that when a = true, the result should be b. When a = false, it should check x next, and if x = true, the result should be y. Now, if both a and x turn out to be false, the compiler knows to proceed to checking p, and if p = true, then the result is q.

But here's the question: what should the result be when all three, a, b, and p turn out to be false?

You haven't told the compiler what to do in that case, so of course it complains!


But why does it complain so cryptically? What does unit have to do with it?

This has to do with a small syntactic relaxation that exists in F# to ease the developers' life. You see, because F# is not a pure functional language, which means that it can have arbitrary side-effects, very often those side-effects don't return any meaningful value, like printf for example:

> printf "foo"
it : unit = ()

The function doesn't have anything good to return, but there has to be some return type, and there is a special type just for this occasion - unit. It's a special type that has exactly one value, so it doesn't mean anything.

Now let's see what happens if I need to put my printf call inside an if: in any if, both then and else branches must have the same type, otherwise it's not clear what the type of the whole if expression should be. Therefore, if my then branch contains a printf, my else branch must also be of type unit. So I am forced to always put this meaningless appendix there:

> if foo then printf "bar" else ()
it : unit = ()

This is annoying. In fact, so annoying it is that the F# language has a special case for it: when my then branch is of type unit, I can omit the else branch altogether, and the compiler will just assume that I meant to say else ():

> if foo then printf "bar"
it : unit = ()

So this is what happens in your case: since you have omitted the else branch, the compiler assumes that all then branches must be of type unit, but they are clearly of type float, so the compiler complains.


To fix this, you need to provide an else branch. Judging by your code, it seems to me that you really had in mind there possible cases: (1) less than 10, (2) between 10 and 100, and (3) everything else. If so, the "everything else" branch should be the else:

if Balance < 10.00 then "Balance is low" 
elif Balance >= 10.00 && Balance <= 100.00 then "Balance is ok" 
else "balance is high"

P.S. after you fix this, you'll have a similar problem in the test function: the two then branches are string, but the else branch is float

Upvotes: 7

Related Questions