Improve error reporting: Trying to pass arguments to a simple value gives confusing error.
What
This code leads to the following error:
let x = 5
x 10
^
error FS0003: This value is not a function and cannot be applied.
Why
Beginners to the language try to read this error and 9/10 times don't understand what it means.
How
Consider replacing the error message with something like:
"x" is a simple value of type "int", and not a function. It does not take in any arguments, but you are trying to pass the value "10" as an argument to it.
Yes this would have help tons of people that I tried to sell on the language
I agree that the error showing the type of the value would be beneficial.
I did a quick look at this. It seemed simple enough because the error information does have a TType, but it hasn't been resolved yet. So, it's going to be more involved.
@TIHan even just saying something like "x" is a simple value, not a function. It does not take in any arguments, but you are trying to pass a value as an argument to it. would be an improvement on what we have now :-)
My suggestion for wording: 'x' is a value of type 'int', which is not a function type. It is invalid to use 'x' as a function, but a value is being passed to it as an argument as if it were a function.
Perhaps it could also give an example of what function types look like.
Note the important difference in making sure to avoid an accusatory tone -- aside from that nitpicking I agree that this is a good change.
@KathleenDollard and I started looking at systematically improving mismatches in argument application
Here is the start of a matrix and "ideal" error messages that begin to follow a system. WIP.
let testv = 1+1
let test0 () = ()
let test1 (x: string) = ()
let test2 (x: string, y: int) = ()
let test11 (x: int) (y: int) = ()
let test21 (x: string, y: int) (z:int) = ()
let test12 (x: string) (y: int, z:int) = ()
let test22 (x: string, y: int) (z: int, w:int) = ()
let gtest1 (x: 'T) = ()
let gtest11 (x: 'T) (y: 'U) = ()
+ members
let v_v_0 = testv ()
// TODAY: This value is not a function and cannot be applied.
// TODO: Can this be better?
let v_v_1 = testv 1
// TODAY: This value is not a function and cannot be applied.
// TODO: Can this be better?
let v_v_2 = testv (1,2)
// TODAY: This value is not a function and cannot be applied.
// TODO: Can this be better?
let v_v_1_1 = testv 1 2
// TODAY: This value is not a function and cannot be applied.
// TODO: Can this be better?
let v0_1 = test0 "text"
// The function 'test0' expects to be invoked as 'test0 ()'. The argument beginning '"text"'
// is unexpected. Consider replacing the argument with '()' or changing the definition of 'test0'.
let v0_1b = test0 "some long text"
// The function 'test0' expects to be invoked as 'test0 ()'. The argument beginning '"some long..."'
// is unexpected. Consider replacing the argument with '()' or changing the definition of 'test0'.
// NOTE: the text "The function has a parameter...." is shown if not enough arguments
// NOTE: the text "The argument is ... unexpected." is shown if too many arguments
// NOTE: the text "The argument beginning...." is shown if too many arguments and long argument text
// NOTE: the text 'or changing...' is only added if the function being called is in the same assembly
let v1_0 = test1 ()
// The function 'test1' expects 1 argument of type 'string'. For example:
// test1 x
// Consider providing 1 argument, or changing the definition of 'test1'.
// NOTE: we show the types of missing arguments, since it helps the user fill them in
// NOTE: we show the names of missing arguments via sample usage, since it helps the user see the call they need to make
let v1_2 = test1 ("a", 1)
// The function 'test1' expects 1 argument of type 'string', but is here given
// 2 arguments as a tuple. Consider removing an argument, or changing the definition of 'test1'.
// NOTE: we show the type of a single argument when a tuple has been passed instead
let v1_11 = test1 "a" "b"
// The function 'test1' expects 1 argument of type 'string', but
// is here given 2 arguments. The argument '"b"' is unexpected. Consider removing an argument,
// or changing the definition of 'test1'.
// NOTE: we show the type of a single argument when extra curried arguments have been provided
let v2_0 = test2 ()
// PROPOSAL:
// The function 'test2' expects 2 tupled arguments of type 'string' and 'int'. For example:
// test2 (x, y)
// Consider providing 2 arguments as a tuple, or changing the definition of 'test2'.
let v2_1 = test2 "a"
// The function 'test2' expects 2 tupled arguments of type 'string' and 'int' and is here given 1. For example:
// test2 (x, y)
// Consider providing 2 arguments as a tuple.
// NOTE: we suppressed "and is here given ..." when a () was provided
let v2_3 = test2 ("abc", 1, 2)
// PROPOSAL:
// The function 'test2' expects 2 tupled arguments and is here given 3. For example:
// test2 (x, y)
// Consider removing an argument.
let v2_11 = test2 "abc" 1
// PROPOSAL:
// The function 'test2' expects 2 tupled arguments and is here given 2 arguments separated
// by spaces (i.e. curried). Consider grouping the arguments using a tuple, for example:
// test2 ("abc", 1)
// NOTE: here we show the tupled form of the expressions provided by the user
// NOTE: if this is too long then just don't show it, e.g. just this:
// Consider grouping the arguments using a tuple.
// TODO: Check whether Elm, Rust etc show expressions coming from the user.
// TODO: Implement this in a way so that whenever we say "Consider .... for example: ..." we offer this as a quickfix
let v11_111 = test11 "abc" 3 5
// The function 'test11' expects 2 arguments separated by spaces (i.e. curried), but
// is here given 3. The argument '5' is unexpected.
// Consider removing an argument, or adding parentheses around an argument, or changing the definition of 'test11'.
let v11_2 = test11 ("abc", 3)
// The function 'test11' expects 2 arguments separated by spaces (i.e. curried), but
// is here given a single argument that is a tuple with two elements. Consider separating the arguments with spaces:
// test11 "abc" 3