我试图创建一个将字符串和整数作为参数/输入的函数。
let rec twoTwo (s : string) (n : int) =
match n with
|0 -> 0
|_ -> s + (twoTwo s (n-1))
printfn "%A" (twoTwo "string" 5)
这里我得到类型int与类型字符串不匹配的错误。这超出了我的原因?它在递归调用中是错误类型错配。
有什么问题?
答案 0 :(得分:7)
这里有一个值得思考的问题:你的功能是什么回归?
如果您查看match
的第一个分支,则返回int
,但在第二个分支中,您使用自己的返回值与{{1}连接},所以返回值必须是一个字符串。
那是哪个?编译器无法判断你的意思,所以它会抱怨。
回复您的评论:
编译器首先读取第一个分支中的零,并在此基础上确定函数的返回值必须为string
。之后,编译器遇到字符串连接,看到已经确定的返回类型不适合,并发出错误。
如果分支被反转,那么逻辑也将被反转:编译器将首先遇到连接,并在此基础上决定返回类型必须是int
。之后,它会遇到零,看到它与先前决定的返回类型不匹配,并抱怨。在这种情况下,错误将为零
那个案子你会抱怨吗?为什么?您期望编译器如何知道错误是零,而不是连接?所有编译器都可能知道两个分支不匹配。它不知道哪一个是正确的。
你认为这是显而易见的,因为你知道你的意图。但编译器并不知道你打算做什么,它只能看到你写的内容。
用另一种语言,你可以称之为"新手友好" ,例如C#(或Java),这种情况不会出现,因为你总是被迫明确指定所有的返回和参数类型。这使得错误更容易理解,因为现在编译器可以判断哪个分支不正确:它是不同意明确声明的返回类型的分支。
但是如果你考虑一下,你只是将问题转移到上游:编译器如何知道显式声明的类型是否正确?编译器只是假定声明的类型是最终的事实,但这只是一个约定。对你来说非常熟悉,是的,但是和按顺序阅读程序一样随意。
但是,如果您觉得这种方法更容易,您也可以在F#中完全使用它。 F#允许(虽然不是需要)在C#所做的所有地方(然后是一些)显式类型规范:
string
实际上,添加显式类型规范是一种常见的调试技术。当我发现自己的类型不匹配错误并不完全明显时,我开始向事物添加类型规范,直到错误成为焦点。