简体   繁体   中英

F# Two Different types of Function Arguments

Im trying to make a function that will take a string and an integer as arguments/input.

let rec twoTwo (s : string) (n : int) = 
  match n with 
  |0 -> 0 
  |_ -> s + (twoTwo s (n-1))

printfn "%A" (twoTwo "string" 5)

Here i get the error that the type int does not match the type string. It is beyond me why? It is in the recursive call that it errors type missmatch.

What is wrong?

Here's a question to think about: what does your function return?

If you look at the first branch of the match , you return an int , but in the second branch you're using your own return value in concatenation with a string , so the return value must be a string.

So which is it? The compiler can't tell which one you meant, so it complains.

In response to your comment:
The compiler first read the zero in the first branch, and on that basis decided that the function's return must be int . After that, the compiler encountered the string concatenation, saw that the already-decided return type doesn't fit, and issued an error.
If the branches were reversed, then the logic would be reversed as well: the compiler would first encounter the concatenation, and on that basis decide that the return type must be string . After that, it would encounter the zero, see that it doesn't match the previously-decided return type, and complain. In this scenario, the error would have been at the zero.
Would you have complained in that case? Why? How do you expect the compiler to know that the error is at the zero, and not at the concatenation? All the compiler can possibly know is that the two branches don't match. It doesn't know which one is correct.

You think that this is obvious, because you know what you meant to do. But the compiler doesn't know what you meant to do, it can only see what you wrote.

In a different language, one you could call more "rookie-friendly" , such as, perhaps, C# (or Java), this situation doesn't arise, because you're always forced to explicitly specify all return and parameter types. This makes the errors a little more comprehensible, because now the compiler can tell which branch is incorrect: it's the one that doesn't agree with the explicitly declared return type.
But if you think about it, you've merely moved the problem upstream: how does the compiler know that the explicitly declared type is correct? The compiler simply assumes that the declared type is the ultimate truth, but that's merely a convention. One that is very familiar to you, yes, - but just as arbitrary as reading the program in order.

However, if you find this approach easier for you, you can totally employ it in F# as well. F# allows (though doesn't require ) explicit type specifications in all places where C# does (and then some):

let rec twoTwo (s : string) (n : int) : string = 
  match n with 
  |0 -> 0   // Error on this line: this expression was expected to have type string, but here has type int
  |_ -> s + (twoTwo s (n-1))

In fact, adding explicit type specifications is a common debugging technique. When I find myself with a type mismatch error that's not entirely obvious, I start adding type specifications to things until the error comes into focus.

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