F#两种不同类型的函数参数

Nul*_*lle 2 string int f# arguments function

我试图创建一个函数,将一个字符串和一个整数作为参数/输入.

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

printfn "%A" (twoTwo "string" 5)
Run Code Online (Sandbox Code Playgroud)

这里我得到类型int与类型字符串不匹配的错误.这超出了我的原因?它在递归调用中是错误类型错配.

怎么了?

Fyo*_*kin 7

这是一个值得思考的问题:你的函数会返回什么?

如果查看第一个分支,则match返回一个int,但在第二个分支中,您使用自己的返回值与a连接string,因此返回值必须是字符串.

那是哪个呢?编译器无法分辨你的意思,所以它会抱怨.

回应你的评论:
编译器首先读取第一个分支中的零,并在此基础上决定函数的返回必须是int.之后,编译器遇到字符串连接,看到已经确定的返回类型不适合,并发出错误.
如果分支被反转,那么逻辑也将被反转:编译器将首先遇到串联,并在此基础上决定返回类型必须string.之后,它会遇到零,看到它与先前决定的返回类型不匹配,并抱怨.在这种情况下,错误将为零.
那个案子你会抱怨吗?为什么?您期望编译器如何知道错误是零,而不是连接?所有编译器都可能知道两个分支不匹配.它不知道哪一个是正确的.

认为这很明显,因为你知道你的意图.但是编译器不知道你打算做什么,它只能看到你写的内容.

在另一种语言中,你可以称之为"菜鸟友好",例如C#(或Java),这种情况不会出现,因为你总是被迫明确指定所有的返回和参数类型.这使得错误更容易理解,因为现在编译器可以判断哪个分支是错误的:它是与显式声明的返回类型不一致的分支.
但是如果你考虑一下,你只是将问题转移到上游:编译器如何知道显式声明的类型是否正确?编译器只是假设声明的类型是最终的事实,但这只是一个约定.对你来说非常熟悉,是的,但是就像按顺序阅读程序一样随意.

但是,如果您觉得这种方法更容易,您也可以在F#中完全使用它.F#允许(但不要求)C#所有地方(然后是一些)的显式类型规范:

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))
Run Code Online (Sandbox Code Playgroud)

实际上,添加显式类型规范是一种常见的调试技术.当我发现自己的类型不匹配错误并不完全明显时,我开始向事物添加类型规范,直到错误成为焦点.