Вот вопрос, о котором нужно подумать: что возвращает ваша функция?
Если вы посмотрите на первой ветви match
, вы возвращаете int
, но во втором отделении вы используете свой собственный возвращаемое значение в конкатенации с string
, так что возвращаемое значение должно быть строкой.
Итак, что это? Компилятор не может сказать, что вы имели в виду, поэтому он жалуется.
В ответ на ваш комментарий:
Составитель первого чтения нуля в первой ветви, и на этом основании решили, что возвращение функции должен быть int
. После этого компилятор столкнулся с конкатенацией строк, увидел, что уже выбранный тип возвращаемого значения не подходит, и выдает сообщение об ошибке.
Если ветви были отменены, то логика также будет отменена: компилятор сначала столкнется с конкатенацией, и на этой основе решит, что тип возврата должен быть string
. После этого он столкнется с нулем, увидит, что он не соответствует ранее заданному типу возврата и жалуется. В этом случае ошибка была бы равна нулю.
Вы бы пожаловались в этом случае? Зачем? Как вы ожидаете, что компилятор узнает, что ошибка находится на нуле, а не на конкатенации? Весь компилятор может знать, что две ветви не совпадают. Он не знает, какой из них правильный.
Вы,, считаете, что это очевидно, потому что вы знаете, что вы хотели сделать. Но компилятор не знает, что вы собираетесь делать, он может видеть только то, что вы написали.
На другом языке, который вы могли бы назвать более "rookie-friendly", например, C# (или Java), эта ситуация не возникает, поскольку вы всегда вынуждены явно указывать все возврат и типы параметров. Это делает ошибки более понятными, потому что теперь компилятор может сказать, какая ветка неверна: это та, которая не согласуется с явно объявленным типом возврата.
Но если вы думаете об этом, вы просто переместили проблему вверх: как компилятор знает, что явно объявленный тип верен? Компилятор просто принимает, что объявленный тип является конечной истиной, но это просто соглашение. Да, это очень хорошо знакомо, но так же произвольно, как чтение программы в порядке.
Однако, если вы найдете этот подход более удобным для вас, вы можете полностью использовать его в F #. F # позволяет (хотя не требует) спецификаций явных типа во всех местах, где С # (и затем некоторыми):
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))
На самом деле, добавив спецификацию явного типа является общим методом отладки.Когда я нахожусь с ошибкой несоответствия типа, которая не совсем очевидна, я начинаю добавлять спецификации типов к вещам, пока ошибка не начнет фокусироваться.
Возможно, лучше сделать 'n'' uint', а не 'int', потому что если отрицательное число передано в него, произойдет сбой. – stt106
@ stt106 Почему в этом случае помощь без знака? Обычный способ справиться с этим заключается в том, чтобы добавить символ 'when' к совпадению шаблонов на счетчике' n' и использовать 'failwith' из оператора catch-all' _ -> с ошибкой «Woot?» '. В конце концов, какие гарантии у вас есть, что отрицательное число передано? Может быть, некоторые Unicode или байты будут переданы. – s952163
@ s952163, если n является единицей типа, тогда компилятор даже не позволит вам передавать байты или Unicode. – stt106