简体   繁体   中英

F# error: expression was expected to have type unit

I'm trying to build a calculator in F#. So, I take input from user regarding operation to be performed. For input 6, it should display menu for scientific operations, however, it says expression was expected to have type unit but has type float. And also in the scientificFun() function, for the last line, it says,'The expression was expected to have float but here has unit'. I'm not sure what it means. Been stuck on this for hours. Any help would be appreciated. Thanks!. The ** or bold lines show where there error is occuring.

open System

let mutable ok = true
while ok do
Console.WriteLine("Choose a operation:\n1.Addition\n2.Substraction\n3.Multiplication\n4.Division\n5.Modulo\n6.Scientific")
let input= Console.ReadLine()  

let add() = 
    Console.WriteLine("Ok, how many numbers?")
    let mutable count = int32(Console.ReadLine())
    let numberArray = Array.create count 0.0
    for i in 0 .. numberArray.Length - 1 do
        let no = float(Console.ReadLine())
        Array.set numberArray i no    
    Array.sum numberArray

let expo() =
    Console.WriteLine("Enter the base")
    let getBase = Console.ReadLine()
    Console.WriteLine("Enter the exponent")
    let getExponent = Console.ReadLine()
    float(getBase) ** float(getExponent)

let sqRoot() = 
    Console.WriteLine("Enter a number")
    let no = float(Console.ReadLine())        
    Math.Sqrt no

let rec fact (n:float) =
    if n < 1.0 then 1.0
    else n * fact (n - 1.0)

let factorial() = 
    Console.WriteLine("Enter a number")
    let no = float(Console.ReadLine())
    fact(no)


let Trigsin() = 
    Console.WriteLine("Enter an angle")
    let angle = float(Console.ReadLine())
    Math.Sin angle
let Trigcos() =
    Console.WriteLine("Enter an angle")
    let angle = float(Console.ReadLine())
    Math.Cos angle
let Trigtan() =
    Console.WriteLine("Enter an angle")
    let angle = float(Console.ReadLine())
    Math.Tan angle

let logicalAnd() =
    Console.WriteLine("Enter first number")
    let first = int32(Console.ReadLine())
    Console.WriteLine("Enter second number")
    let second = int32(Console.ReadLine())
    float(first &&& second) 
let logicalOr() =
    Console.WriteLine("Enter first number")
    let first = int(Console.ReadLine())
    Console.WriteLine("Enter second number")
    let second = int(Console.ReadLine())
    float(first ||| second)
let logicalNot()=
    Console.WriteLine("Enter a number")
    let first = int32(Console.ReadLine())
    float(~~~first)
let sub x y = x - y 
let mul x y = x * y
let div x y = x / y
let MOD x y = x % y

let scientificFun() = 
    printfn("1.Exponential\n2.Square Root\n3.Factorial\n4.sin()\n5.cos()\n6.tan()\n7.AND\n8.OR\n9.NOT")
    let scientificInput = Console.ReadLine()
    match scientificInput with
    |"1" -> expo()
    |"2" -> sqRoot()
    |"3" -> factorial()
    |"4" -> Trigsin()
    |"5" -> Trigcos()
    |"6" -> Trigtan()
    |"7" -> logicalAnd()
    |"8" -> logicalOr()
    |"9" -> logicalNot()
    | _ -> **printfn("Choose between 1 - 9")**



match input with
| "1" -> printfn("The Result is: %f") (add())
//| "2" -> printfn("The Result is: %f") (sub A B)
//| "3" -> printfn("The Result is: %f") (mul A B)
///| "4" -> printfn("The Result is: %f") (div A B)
//| "5" -> printfn("The Result is: %f") (MOD A B)
| "6" -> **scientificFun()**
| _-> printfn("Choose between 1 and 6")
Console.WriteLine("Would you like to use the calculator again? y/n")
let ans = Console.ReadLine()
if ans = "n" then
    ok <- false
else Console.Clear()

The expression was expected to have float but here has unit

This is a very important message from the compiler and you should try understanding why it says so, and what it means. In very simple terms functions map values from one domain to another.

If you have a function, for example:

let makeStr (x:int) =
    string x

Its signature will tell you what are its input and output types: val makeStr : x:int -> string . In this case it takes an int and returns it as string. So, this works: makeStr 10 but this won't makeStr 10. , it will fail with the following message:

error FS0001: This expression was expected to have type int but here has type float

In your specific case you can check the signature of scientificFun() . Both VSCode and VS2015 will show you that it's val scientificFun : (unit -> float) . Which is a function that takes no input (unit) and returns a float. However in choice _, you have _ -> printfn("Choose between 1 - 9") . printfn prints to the console, and doesn't return a value, or rather it returns () (unit), which indicates that it has a side-effect, of, well, printing to the console. You cannot return a float from one branch and something else from another. There are several ways to work around this, one of which was suggested by @Funk, basically wrap your return values in an Option type, which is probably the best option ;^). But in this case let's cheat a bit, and fix up your function in a quick and dirty way:

Change the last line of the match expression to this: | _ -> printfn("Choose between 1 - 9");0. | _ -> printfn("Choose between 1 - 9");0. Here the wildcard becomes a compound expression, that prints, but finally returns 0., which is a float, and the F# compiler is happy.

Then you still need to fix option 6, in the final match. If you look above you can see that all other branches print to the console, so should return unit, but scientificFun's signature will tell you it returns float, so which one is it? Just change the branch to look like all the other expressions: | "6" -> printfn("The Result is: %f") <| scientificFun() | "6" -> printfn("The Result is: %f") <| scientificFun()

Once you got this working I suggest that maybe you post this onto CodeReview where it can be reworked with a more idiomatic F#/functional style.

Also these references should help you along the way:

Match Expressions
Pattern Matching
Match Expressions 2
F# Expressions and Syntax
Thinking Functionally

Add 1

You can also make the ScientificFun() a recursive function that calls itself.

let rec scientificFun() = 
    printfn("1.Exponential\n2.Square Root\n3.Factorial\n4.sin()\n5.cos()\n6.tan()\n7.AND\n8.OR\n9.NOT")
    let scientificInput = Console.ReadLine()
    match scientificInput with
    |"1" -> expo()
    |"2" -> sqRoot()
    |"3" -> factorial()
    |"4" -> Trigsin()
    |"5" -> Trigcos()
    |"6" -> Trigtan()
    |"7" -> logicalAnd()
    |"8" -> logicalOr()
    |"9" -> logicalNot()
    | _ -> scientificFun()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM